From ed6066e57f26c3749f7458cc0953d69a520aafd3 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 10:31:40 -0700 Subject: [PATCH 01/52] Move settings -> args parsing to its own function --- emscripten.py | 85 ++++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/emscripten.py b/emscripten.py index 9fca0eba717ab..ed04e5c6d7727 100755 --- a/emscripten.py +++ b/emscripten.py @@ -108,45 +108,7 @@ def get_and_parse_backend(infile, settings, temp_files, DEBUG): with temp_files.get_file('.4.js') as temp_js: backend_compiler = os.path.join(shared.LLVM_ROOT, 'llc') backend_args = [backend_compiler, infile, '-march=js', '-filetype=asm', '-o', temp_js] - if settings['PRECISE_F32']: - backend_args += ['-emscripten-precise-f32'] - if settings['USE_PTHREADS']: - backend_args += ['-emscripten-enable-pthreads'] - if settings['WARN_UNALIGNED']: - backend_args += ['-emscripten-warn-unaligned'] - if settings['RESERVED_FUNCTION_POINTERS'] > 0: - backend_args += ['-emscripten-reserved-function-pointers=%d' % settings['RESERVED_FUNCTION_POINTERS']] - if settings['ASSERTIONS'] > 0: - backend_args += ['-emscripten-assertions=%d' % settings['ASSERTIONS']] - if settings['ALIASING_FUNCTION_POINTERS'] == 0: - backend_args += ['-emscripten-no-aliasing-function-pointers'] - if settings['EMULATED_FUNCTION_POINTERS']: - backend_args += ['-emscripten-emulated-function-pointers'] - if settings['RELOCATABLE']: - backend_args += ['-emscripten-relocatable'] - backend_args += ['-emscripten-global-base=0'] - elif settings['GLOBAL_BASE'] >= 0: - backend_args += ['-emscripten-global-base=%d' % settings['GLOBAL_BASE']] - if settings['SIDE_MODULE']: - backend_args += ['-emscripten-side-module'] - backend_args += ['-emscripten-stack-size=%d' % settings['TOTAL_STACK']] - backend_args += ['-O' + str(settings['OPT_LEVEL'])] - if settings['DISABLE_EXCEPTION_CATCHING'] != 1: - backend_args += ['-enable-emscripten-cpp-exceptions'] - if settings['DISABLE_EXCEPTION_CATCHING'] == 2: - backend_args += ['-emscripten-cpp-exceptions-whitelist=' + ','.join(settings['EXCEPTION_CATCHING_WHITELIST'] or ['fake'])] - if settings['ASYNCIFY']: - backend_args += ['-emscripten-asyncify'] - backend_args += ['-emscripten-asyncify-functions=' + ','.join(settings['ASYNCIFY_FUNCTIONS'])] - backend_args += ['-emscripten-asyncify-whitelist=' + ','.join(settings['ASYNCIFY_WHITELIST'])] - if settings['NO_EXIT_RUNTIME']: - backend_args += ['-emscripten-no-exit-runtime'] - if settings['BINARYEN']: - backend_args += ['-emscripten-wasm'] - if shared.Building.is_wasm_only(): - backend_args += ['-emscripten-only-wasm'] - if settings['CYBERDWARF']: - backend_args += ['-enable-cyberdwarf'] + backend_args += backend_args_for_settings(settings) if DEBUG: logging.debug('emscript: llvm backend: ' + ' '.join(backend_args)) @@ -220,6 +182,51 @@ def fix_dot_zero(m): return funcs, metadata, mem_init +def backend_args_for_settings(settings): + """Create args for asm.js backend from settings dict""" + args = [ + '-emscripten-stack-size=%d' % settings['TOTAL_STACK'], + '-O' + str(settings['OPT_LEVEL']), + ] + if settings['PRECISE_F32']: + args += ['-emscripten-precise-f32'] + if settings['USE_PTHREADS']: + args += ['-emscripten-enable-pthreads'] + if settings['WARN_UNALIGNED']: + args += ['-emscripten-warn-unaligned'] + if settings['RESERVED_FUNCTION_POINTERS'] > 0: + args += ['-emscripten-reserved-function-pointers=%d' % settings['RESERVED_FUNCTION_POINTERS']] + if settings['ASSERTIONS'] > 0: + args += ['-emscripten-assertions=%d' % settings['ASSERTIONS']] + if settings['ALIASING_FUNCTION_POINTERS'] == 0: + args += ['-emscripten-no-aliasing-function-pointers'] + if settings['EMULATED_FUNCTION_POINTERS']: + args += ['-emscripten-emulated-function-pointers'] + if settings['RELOCATABLE']: + args += ['-emscripten-relocatable'] + args += ['-emscripten-global-base=0'] + elif settings['GLOBAL_BASE'] >= 0: + args += ['-emscripten-global-base=%d' % settings['GLOBAL_BASE']] + if settings['SIDE_MODULE']: + args += ['-emscripten-side-module'] + if settings['DISABLE_EXCEPTION_CATCHING'] != 1: + args += ['-enable-emscripten-cpp-exceptions'] + if settings['DISABLE_EXCEPTION_CATCHING'] == 2: + args += ['-emscripten-cpp-exceptions-whitelist=' + ','.join(settings['EXCEPTION_CATCHING_WHITELIST'] or ['fake'])] + if settings['ASYNCIFY']: + args += ['-emscripten-asyncify'] + args += ['-emscripten-asyncify-functions=' + ','.join(settings['ASYNCIFY_FUNCTIONS'])] + args += ['-emscripten-asyncify-whitelist=' + ','.join(settings['ASYNCIFY_WHITELIST'])] + if settings['NO_EXIT_RUNTIME']: + args += ['-emscripten-no-exit-runtime'] + if settings['BINARYEN']: + args += ['-emscripten-wasm'] + if shared.Building.is_wasm_only(): + args += ['-emscripten-only-wasm'] + if settings['CYBERDWARF']: + args += ['-enable-cyberdwarf'] + return args + def compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG): # js compiler From 3c56b9862d75ceea56e9fea367d26dc124bd6de2 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 11:15:36 -0700 Subject: [PATCH 02/52] Compile js separately from parsing its output --- emscripten.py | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/emscripten.py b/emscripten.py index ed04e5c6d7727..f28ce8a0d06f6 100755 --- a/emscripten.py +++ b/emscripten.py @@ -88,7 +88,8 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, # memory to be reclaimed with ToolchainProfiler.profile_block('get_and_parse_backend'): - funcs, metadata, mem_init = get_and_parse_backend(infile, settings, temp_files, DEBUG) + backend_output = compile_js(infile, settings, temp_files, DEBUG) + funcs, metadata, mem_init = get_and_parse_backend(backend_output, settings, DEBUG) with ToolchainProfiler.profile_block('compiler_glue'): glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) @@ -104,24 +105,7 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, if not success: shared.try_delete(outfile.name) # remove partial output -def get_and_parse_backend(infile, settings, temp_files, DEBUG): - with temp_files.get_file('.4.js') as temp_js: - backend_compiler = os.path.join(shared.LLVM_ROOT, 'llc') - backend_args = [backend_compiler, infile, '-march=js', '-filetype=asm', '-o', temp_js] - backend_args += backend_args_for_settings(settings) - - if DEBUG: - logging.debug('emscript: llvm backend: ' + ' '.join(backend_args)) - t = time.time() - with ToolchainProfiler.profile_block('emscript_llvm_backend'): - shared.jsrun.timeout_run(subprocess.Popen(backend_args, stdout=subprocess.PIPE)) - if DEBUG: - logging.debug(' emscript: llvm backend took %s seconds' % (time.time() - t)) - - # Split up output - backend_output = open(temp_js).read() - #if DEBUG: print >> sys.stderr, backend_output - +def get_and_parse_backend(backend_output, settings, DEBUG): start_funcs_marker = '// EMSCRIPTEN_START_FUNCTIONS' end_funcs_marker = '// EMSCRIPTEN_END_FUNCTIONS' metadata_split_marker = '// EMSCRIPTEN_METADATA' @@ -182,6 +166,28 @@ def fix_dot_zero(m): return funcs, metadata, mem_init + +def compile_js(infile, settings, temp_files, DEBUG): + """Compile infile with asm.js backend, return the contents of the compiled js""" + with temp_files.get_file('.4.js') as temp_js: + backend_compiler = os.path.join(shared.LLVM_ROOT, 'llc') + backend_args = [backend_compiler, infile, '-march=js', '-filetype=asm', '-o', temp_js] + backend_args += backend_args_for_settings(settings) + + if DEBUG: + logging.debug('emscript: llvm backend: ' + ' '.join(backend_args)) + t = time.time() + with ToolchainProfiler.profile_block('emscript_llvm_backend'): + shared.jsrun.timeout_run(subprocess.Popen(backend_args, stdout=subprocess.PIPE)) + if DEBUG: + logging.debug(' emscript: llvm backend took %s seconds' % (time.time() - t)) + + # Split up output + backend_output = open(temp_js).read() + #if DEBUG: print >> sys.stderr, backend_output + return backend_output + + def backend_args_for_settings(settings): """Create args for asm.js backend from settings dict""" args = [ @@ -227,6 +233,7 @@ def backend_args_for_settings(settings): args += ['-enable-cyberdwarf'] return args + def compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG): # js compiler From 312fcb6ee18072e266d7809052478ba3f313e0bc Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 11:28:19 -0700 Subject: [PATCH 03/52] Separate parsing metadata from metadata postprocessing --- emscripten.py | 134 ++++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 63 deletions(-) diff --git a/emscripten.py b/emscripten.py index f28ce8a0d06f6..321a2619d7b88 100755 --- a/emscripten.py +++ b/emscripten.py @@ -89,7 +89,9 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, with ToolchainProfiler.profile_block('get_and_parse_backend'): backend_output = compile_js(infile, settings, temp_files, DEBUG) - funcs, metadata, mem_init = get_and_parse_backend(backend_output, settings, DEBUG) + funcs, metadata, mem_init = parse_backend_output(backend_output, DEBUG) + fixup_metadata_tables(metadata, settings) + funcs = fixup_functions(funcs, metadata, settings) with ToolchainProfiler.profile_block('compiler_glue'): glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) @@ -105,68 +107,6 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, if not success: shared.try_delete(outfile.name) # remove partial output -def get_and_parse_backend(backend_output, settings, DEBUG): - start_funcs_marker = '// EMSCRIPTEN_START_FUNCTIONS' - end_funcs_marker = '// EMSCRIPTEN_END_FUNCTIONS' - metadata_split_marker = '// EMSCRIPTEN_METADATA' - - start_funcs = backend_output.index(start_funcs_marker) - end_funcs = backend_output.rindex(end_funcs_marker) - metadata_split = backend_output.rindex(metadata_split_marker) - - funcs = backend_output[start_funcs+len(start_funcs_marker):end_funcs] - metadata_raw = backend_output[metadata_split+len(metadata_split_marker):] - #if DEBUG: print >> sys.stderr, "METAraw", metadata_raw - try: - metadata = json.loads(metadata_raw) - except Exception, e: - logging.error('emscript: failure to parse metadata output from compiler backend. raw output is: \n' + metadata_raw) - raise e - mem_init = backend_output[end_funcs+len(end_funcs_marker):metadata_split] - #if DEBUG: print >> sys.stderr, "FUNCS", funcs - #if DEBUG: print >> sys.stderr, "META", metadata - #if DEBUG: print >> sys.stderr, "meminit", mem_init - - # if emulating pointer casts, force all tables to the size of the largest - if settings['EMULATE_FUNCTION_POINTER_CASTS']: - max_size = 0 - for k, v in metadata['tables'].iteritems(): - max_size = max(max_size, v.count(',')+1) - for k, v in metadata['tables'].iteritems(): - curr = v.count(',')+1 - if curr < max_size: - metadata['tables'][k] = v.replace(']', (',0'*(max_size - curr)) + ']') - - if settings['SIDE_MODULE']: - for k in metadata['tables'].keys(): - metadata['tables'][k] = metadata['tables'][k].replace('var FUNCTION_TABLE_', 'var SIDE_FUNCTION_TABLE_') - - # function table masks - - table_sizes = {} - for k, v in metadata['tables'].iteritems(): - table_sizes[k] = str(v.count(',')) # undercounts by one, but that is what we want - #if settings['ASSERTIONS'] >= 2 and table_sizes[k] == 0: - # print >> sys.stderr, 'warning: no function pointers with signature ' + k + ', but there is a call, which will abort if it occurs (this can result from undefined behavior, check for compiler warnings on your source files and consider -Werror)' - funcs = re.sub(r"#FM_(\w+)#", lambda m: table_sizes[m.groups(0)[0]], funcs) - - # fix +float into float.0, if not running js opts - if not settings['RUNNING_JS_OPTS']: - def fix_dot_zero(m): - num = m.group(3) - # TODO: handle 0x floats? - if num.find('.') < 0: - e = num.find('e') - if e < 0: - num += '.0' - else: - num = num[:e] + '.0' + num[e:] - return m.group(1) + m.group(2) + num - funcs = re.sub(r'([(=,+\-*/%<>:?] *)\+(-?)((0x)?[0-9a-f]*\.?[0-9]+([eE][-+]?[0-9]+)?)', fix_dot_zero, funcs) - - return funcs, metadata, mem_init - - def compile_js(infile, settings, temp_files, DEBUG): """Compile infile with asm.js backend, return the contents of the compiled js""" with temp_files.get_file('.4.js') as temp_js: @@ -234,6 +174,74 @@ def backend_args_for_settings(settings): return args +def parse_backend_output(backend_output, DEBUG): + start_funcs_marker = '// EMSCRIPTEN_START_FUNCTIONS' + end_funcs_marker = '// EMSCRIPTEN_END_FUNCTIONS' + metadata_split_marker = '// EMSCRIPTEN_METADATA' + + start_funcs = backend_output.index(start_funcs_marker) + end_funcs = backend_output.rindex(end_funcs_marker) + metadata_split = backend_output.rindex(metadata_split_marker) + + funcs = backend_output[start_funcs+len(start_funcs_marker):end_funcs] + metadata_raw = backend_output[metadata_split+len(metadata_split_marker):] + mem_init = backend_output[end_funcs+len(end_funcs_marker):metadata_split] + + try: + #if DEBUG: print >> sys.stderr, "METAraw", metadata_raw + metadata = json.loads(metadata_raw) + except Exception, e: + logging.error('emscript: failure to parse metadata output from compiler backend. raw output is: \n' + metadata_raw) + raise e + + #if DEBUG: print >> sys.stderr, "FUNCS", funcs + #if DEBUG: print >> sys.stderr, "META", metadata + #if DEBUG: print >> sys.stderr, "meminit", mem_init + return funcs, metadata, mem_init + + +def fixup_metadata_tables(metadata, settings): + # if emulating pointer casts, force all tables to the size of the largest + if settings['EMULATE_FUNCTION_POINTER_CASTS']: + max_size = 0 + for k, v in metadata['tables'].iteritems(): + max_size = max(max_size, v.count(',')+1) + for k, v in metadata['tables'].iteritems(): + curr = v.count(',')+1 + if curr < max_size: + metadata['tables'][k] = v.replace(']', (',0'*(max_size - curr)) + ']') + + if settings['SIDE_MODULE']: + for k in metadata['tables'].keys(): + metadata['tables'][k] = metadata['tables'][k].replace('var FUNCTION_TABLE_', 'var SIDE_FUNCTION_TABLE_') + + +def fixup_functions(funcs, metadata, settings): + # function table masks + table_sizes = {} + for k, v in metadata['tables'].iteritems(): + table_sizes[k] = str(v.count(',')) # undercounts by one, but that is what we want + #if settings['ASSERTIONS'] >= 2 and table_sizes[k] == 0: + # print >> sys.stderr, 'warning: no function pointers with signature ' + k + ', but there is a call, which will abort if it occurs (this can result from undefined behavior, check for compiler warnings on your source files and consider -Werror)' + funcs = re.sub(r"#FM_(\w+)#", lambda m: table_sizes[m.groups(0)[0]], funcs) + + # fix +float into float.0, if not running js opts + if not settings['RUNNING_JS_OPTS']: + def fix_dot_zero(m): + num = m.group(3) + # TODO: handle 0x floats? + if num.find('.') < 0: + e = num.find('e') + if e < 0: + num += '.0' + else: + num = num[:e] + '.0' + num[e:] + return m.group(1) + m.group(2) + num + funcs = re.sub(r'([(=,+\-*/%<>:?] *)\+(-?)((0x)?[0-9a-f]*\.?[0-9]+([eE][-+]?[0-9]+)?)', fix_dot_zero, funcs) + + return funcs + + def compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG): # js compiler From 38819735f7ecf84949b85c93e80120df5524d77d Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 12:02:34 -0700 Subject: [PATCH 04/52] Split out syscall filesystem disabling, simplify logic --- emscripten.py | 152 ++++++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 73 deletions(-) diff --git a/emscripten.py b/emscripten.py index 321a2619d7b88..21c642ae0c15f 100755 --- a/emscripten.py +++ b/emscripten.py @@ -243,86 +243,92 @@ def fix_dot_zero(m): def compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG): - # js compiler + if DEBUG: + logging.debug('emscript: js compiler glue') + t = time.time() - if DEBUG: - logging.debug('emscript: js compiler glue') - t = time.time() + # Settings changes + i64_funcs = ['i64Add', 'i64Subtract', '__muldi3', '__divdi3', '__udivdi3', '__remdi3', '__uremdi3'] + for i64_func in i64_funcs: + if i64_func in metadata['declares']: + settings['PRECISE_I64_MATH'] = 2 + break - # Settings changes - i64_funcs = ['i64Add', 'i64Subtract', '__muldi3', '__divdi3', '__udivdi3', '__remdi3', '__uremdi3'] - for i64_func in i64_funcs: - if i64_func in metadata['declares']: - settings['PRECISE_I64_MATH'] = 2 - break + metadata['declares'] = filter(lambda i64_func: i64_func not in ['getHigh32', 'setHigh32'], metadata['declares']) # FIXME: do these one by one as normal js lib funcs - metadata['declares'] = filter(lambda i64_func: i64_func not in ['getHigh32', 'setHigh32'], metadata['declares']) # FIXME: do these one by one as normal js lib funcs - - # Syscalls optimization. Our syscalls are static, and so if we see a very limited set of them - in particular, - # no open() syscall and just simple writing - then we don't need full filesystem support. - # If FORCE_FILESYSTEM is set, we can't do this. We also don't do it if INCLUDE_FULL_LIBRARY, since - # not including the filesystem would mean not including the full JS libraries, and the same for - # MAIN_MODULE since a side module might need the filesystem. - if not settings['NO_FILESYSTEM'] and not settings['FORCE_FILESYSTEM'] and not settings['INCLUDE_FULL_LIBRARY'] and not settings['MAIN_MODULE']: - syscall_prefix = '__syscall' - syscalls = filter(lambda declare: declare.startswith(syscall_prefix), metadata['declares']) - def is_int(x): - try: - int(x) - return True - except: - return False - syscalls = filter(lambda declare: is_int(declare[len(syscall_prefix):]), syscalls) - syscalls = map(lambda declare: int(declare[len(syscall_prefix):]), syscalls) - if set(syscalls).issubset(set([6, 54, 140, 146])): # close, ioctl, llseek, writev - if DEBUG: logging.debug('very limited syscalls (%s) so disabling full filesystem support' % ', '.join(map(str, syscalls))) - settings['NO_FILESYSTEM'] = 1 + optimize_syscalls(metadata['declares'], settings, DEBUG) - if settings['CYBERDWARF']: - settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'].append("cyberdwarf_Debugger") - settings['EXPORTED_FUNCTIONS'].append("cyberdwarf_Debugger") + if settings['CYBERDWARF']: + settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'].append("cyberdwarf_Debugger") + settings['EXPORTED_FUNCTIONS'].append("cyberdwarf_Debugger") - # Integrate info from backend - if settings['SIDE_MODULE']: - settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] = [] # we don't need any JS library contents in side modules - settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] = list( - set(settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] + map(shared.JS.to_nice_ident, metadata['declares'])).difference( - map(lambda x: x[1:], metadata['implementedFunctions']) - ) - ) + map(lambda x: x[1:], metadata['externs']) - if metadata['simd']: - settings['SIMD'] = 1 - if metadata['cantValidate'] and settings['ASM_JS'] != 2: - logging.warning('disabling asm.js validation due to use of non-supported features: ' + metadata['cantValidate']) - settings['ASM_JS'] = 2 - - settings['MAX_GLOBAL_ALIGN'] = metadata['maxGlobalAlign'] - - settings['IMPLEMENTED_FUNCTIONS'] = metadata['implementedFunctions'] - - assert not (metadata['simd'] and settings['SPLIT_MEMORY']), 'SIMD is used, but not supported in SPLIT_MEMORY' - - # Save settings to a file to work around v8 issue 1579 - with temp_files.get_file('.txt') as settings_file: - def save_settings(): - global settings_text - settings_text = json.dumps(settings, sort_keys=True) - s = open(settings_file, 'w') - s.write(settings_text) - s.close() - save_settings() - - # Call js compiler - out = jsrun.run_js(path_from_root('src', 'compiler.js'), compiler_engine, - [settings_file] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, - cwd=path_from_root('src'), error_limit=300) - assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' - glue, forwarded_data = out.split('//FORWARDED_DATA:') + # Integrate info from backend + if settings['SIDE_MODULE']: + settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] = [] # we don't need any JS library contents in side modules + settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] = list( + set(settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] + map(shared.JS.to_nice_ident, metadata['declares'])).difference( + map(lambda x: x[1:], metadata['implementedFunctions']) + ) + ) + map(lambda x: x[1:], metadata['externs']) + if metadata['simd']: + settings['SIMD'] = 1 + if metadata['cantValidate'] and settings['ASM_JS'] != 2: + logging.warning('disabling asm.js validation due to use of non-supported features: ' + metadata['cantValidate']) + settings['ASM_JS'] = 2 - if DEBUG: - logging.debug(' emscript: glue took %s seconds' % (time.time() - t)) + settings['MAX_GLOBAL_ALIGN'] = metadata['maxGlobalAlign'] + + settings['IMPLEMENTED_FUNCTIONS'] = metadata['implementedFunctions'] + + assert not (metadata['simd'] and settings['SPLIT_MEMORY']), 'SIMD is used, but not supported in SPLIT_MEMORY' + + # Save settings to a file to work around v8 issue 1579 + with temp_files.get_file('.txt') as settings_file: + def save_settings(): + global settings_text + settings_text = json.dumps(settings, sort_keys=True) + s = open(settings_file, 'w') + s.write(settings_text) + s.close() + save_settings() + + # Call js compiler + out = jsrun.run_js(path_from_root('src', 'compiler.js'), compiler_engine, + [settings_file] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, + cwd=path_from_root('src'), error_limit=300) + assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' + glue, forwarded_data = out.split('//FORWARDED_DATA:') + + if DEBUG: + logging.debug(' emscript: glue took %s seconds' % (time.time() - t)) + + return glue, forwarded_data + + +def optimize_syscalls(declares, settings, DEBUG): + """Our syscalls are static, and so if we see a very limited set of them - in particular, + no open() syscall and just simple writing - then we don't need full filesystem support. + If FORCE_FILESYSTEM is set, we can't do this. We also don't do it if INCLUDE_FULL_LIBRARY, since + not including the filesystem would mean not including the full JS libraries, and the same for + MAIN_MODULE since a side module might need the filesystem. + """ + relevant_settings = ['NO_FILESYSTEM', 'FORCE_FILESYSTEM', 'INCLUDE_FULL_LIBRARY', 'MAIN_MODULE'] + if all([not settings[s] for s in relevant_settings]): + syscall_prefix = '__syscall' + syscall_numbers = [d[len(syscall_prefix):] for d in declares if d.startswith(syscall_prefix)] + syscalls = [int(s) for s in syscall_numbers if is_int(s)] + if set(syscalls).issubset(set([6, 54, 140, 146])): # close, ioctl, llseek, writev + if DEBUG: logging.debug('very limited syscalls (%s) so disabling full filesystem support' % ', '.join(map(str, syscalls))) + settings['NO_FILESYSTEM'] = 1 + + +def is_int(x): + try: + int(x) + return True + except: + return False - return glue, forwarded_data def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG): From f907d36202aa587ccfd071ef1b4a07b69bb81840 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 15:00:30 -0700 Subject: [PATCH 05/52] Split out compiler_glue --- emscripten.py | 84 ++++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/emscripten.py b/emscripten.py index 21c642ae0c15f..be99c7aa0cd1d 100755 --- a/emscripten.py +++ b/emscripten.py @@ -254,10 +254,51 @@ def compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DE settings['PRECISE_I64_MATH'] = 2 break - metadata['declares'] = filter(lambda i64_func: i64_func not in ['getHigh32', 'setHigh32'], metadata['declares']) # FIXME: do these one by one as normal js lib funcs + # FIXME: do these one by one as normal js lib funcs + metadata['declares'] = filter(lambda i64_func: i64_func not in ['getHigh32', 'setHigh32'], metadata['declares']) optimize_syscalls(metadata['declares'], settings, DEBUG) + update_settings_glue(settings, metadata) + assert not (metadata['simd'] and settings['SPLIT_MEMORY']), 'SIMD is used, but not supported in SPLIT_MEMORY' + + out = compile_settings(compiler_engine, settings, libraries, temp_files) + assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' + glue, forwarded_data = out.split('//FORWARDED_DATA:') + + if DEBUG: + logging.debug(' emscript: glue took %s seconds' % (time.time() - t)) + + return glue, forwarded_data + + +def optimize_syscalls(declares, settings, DEBUG): + """Disables filesystem if only a limited subset of syscalls is used. + + Our syscalls are static, and so if we see a very limited set of them - in particular, + no open() syscall and just simple writing - then we don't need full filesystem support. + If FORCE_FILESYSTEM is set, we can't do this. We also don't do it if INCLUDE_FULL_LIBRARY, since + not including the filesystem would mean not including the full JS libraries, and the same for + MAIN_MODULE since a side module might need the filesystem. + """ + relevant_settings = ['NO_FILESYSTEM', 'FORCE_FILESYSTEM', 'INCLUDE_FULL_LIBRARY', 'MAIN_MODULE'] + if all([not settings[s] for s in relevant_settings]): + syscall_prefix = '__syscall' + syscall_numbers = [d[len(syscall_prefix):] for d in declares if d.startswith(syscall_prefix)] + syscalls = [int(s) for s in syscall_numbers if is_int(s)] + if set(syscalls).issubset(set([6, 54, 140, 146])): # close, ioctl, llseek, writev + if DEBUG: logging.debug('very limited syscalls (%s) so disabling full filesystem support' % ', '.join(map(str, syscalls))) + settings['NO_FILESYSTEM'] = 1 + +def is_int(x): + try: + int(x) + return True + except: + return False + + +def update_settings_glue(settings, metadata): if settings['CYBERDWARF']: settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'].append("cyberdwarf_Debugger") settings['EXPORTED_FUNCTIONS'].append("cyberdwarf_Debugger") @@ -280,8 +321,8 @@ def compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DE settings['IMPLEMENTED_FUNCTIONS'] = metadata['implementedFunctions'] - assert not (metadata['simd'] and settings['SPLIT_MEMORY']), 'SIMD is used, but not supported in SPLIT_MEMORY' +def compile_settings(compiler_engine, settings, libraries, temp_files): # Save settings to a file to work around v8 issue 1579 with temp_files.get_file('.txt') as settings_file: def save_settings(): @@ -293,45 +334,12 @@ def save_settings(): save_settings() # Call js compiler - out = jsrun.run_js(path_from_root('src', 'compiler.js'), compiler_engine, - [settings_file] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, - cwd=path_from_root('src'), error_limit=300) - assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' - glue, forwarded_data = out.split('//FORWARDED_DATA:') - - if DEBUG: - logging.debug(' emscript: glue took %s seconds' % (time.time() - t)) - - return glue, forwarded_data - - -def optimize_syscalls(declares, settings, DEBUG): - """Our syscalls are static, and so if we see a very limited set of them - in particular, - no open() syscall and just simple writing - then we don't need full filesystem support. - If FORCE_FILESYSTEM is set, we can't do this. We also don't do it if INCLUDE_FULL_LIBRARY, since - not including the filesystem would mean not including the full JS libraries, and the same for - MAIN_MODULE since a side module might need the filesystem. - """ - relevant_settings = ['NO_FILESYSTEM', 'FORCE_FILESYSTEM', 'INCLUDE_FULL_LIBRARY', 'MAIN_MODULE'] - if all([not settings[s] for s in relevant_settings]): - syscall_prefix = '__syscall' - syscall_numbers = [d[len(syscall_prefix):] for d in declares if d.startswith(syscall_prefix)] - syscalls = [int(s) for s in syscall_numbers if is_int(s)] - if set(syscalls).issubset(set([6, 54, 140, 146])): # close, ioctl, llseek, writev - if DEBUG: logging.debug('very limited syscalls (%s) so disabling full filesystem support' % ', '.join(map(str, syscalls))) - settings['NO_FILESYSTEM'] = 1 - - -def is_int(x): - try: - int(x) - return True - except: - return False + return jsrun.run_js(path_from_root('src', 'compiler.js'), compiler_engine, + [settings_file] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, + cwd=path_from_root('src'), error_limit=300) def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG): - if DEBUG: logging.debug('emscript: python processing: function tables and exports') t = time.time() From 8687770632d906e8a3f074bb7f4f9229a3161c9f Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 15:14:55 -0700 Subject: [PATCH 06/52] Remove last_forwarded_json, replace it with just forwarded_json, which is another reference to the same dict --- emscripten.py | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/emscripten.py b/emscripten.py index be99c7aa0cd1d..abb9b6a5a688a 100755 --- a/emscripten.py +++ b/emscripten.py @@ -96,9 +96,9 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) with ToolchainProfiler.profile_block('function_tables_and_exports'): - post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, last_forwarded_json = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) + post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) with ToolchainProfiler.profile_block('finalize_output'): - finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, last_forwarded_json, settings, outfile, DEBUG) + finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG) success = True @@ -347,11 +347,11 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, access_quote = access_quoter(settings) quote = quoter(settings) - last_forwarded_json = forwarded_json = json.loads(forwarded_data) + forwarded_json = json.loads(forwarded_data) # merge in information from llvm backend - last_forwarded_json['Functions']['tables'] = metadata['tables'] + forwarded_json['Functions']['tables'] = metadata['tables'] pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') @@ -392,7 +392,7 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented all_exported_functions.add('_' + additional_export) if settings['EXPORT_FUNCTION_TABLES']: - for table in last_forwarded_json['Functions']['tables'].values(): + for table in forwarded_json['Functions']['tables'].values(): for func in table.split('[')[1].split(']')[0].split(','): if func[0] == '_': all_exported_functions.add(func) @@ -455,7 +455,7 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, # when emulating function pointer casts, we need to know what is the target of each pointer if settings['EMULATE_FUNCTION_POINTER_CASTS']: function_pointer_targets = {} - for sig, table in last_forwarded_json['Functions']['tables'].iteritems(): + for sig, table in forwarded_json['Functions']['tables'].iteritems(): start = table.index('[') end = table.rindex(']') body = table[start+1:end].split(',') @@ -476,9 +476,9 @@ def move_preasm(m): class Counter: i = 0 j = 0 - if 'pre' in last_forwarded_json['Functions']['tables']: - pre_tables = last_forwarded_json['Functions']['tables']['pre'] - del last_forwarded_json['Functions']['tables']['pre'] + if 'pre' in forwarded_json['Functions']['tables']: + pre_tables = forwarded_json['Functions']['tables']['pre'] + del forwarded_json['Functions']['tables']['pre'] else: pre_tables = '' @@ -596,7 +596,7 @@ def make_emulated_param(i): body = ','.join(map(fix_item, body)) return ('\n'.join(Counter.pre), ''.join([raw[:start+1], body, raw[end:]])) - infos = [make_table(sig, raw) for sig, raw in last_forwarded_json['Functions']['tables'].iteritems()] + infos = [make_table(sig, raw) for sig, raw in forwarded_json['Functions']['tables'].iteritems()] Counter.pre = [] function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n' @@ -606,7 +606,7 @@ def make_emulated_param(i): asm_setup = '' if settings['ASSERTIONS'] >= 2: - for sig in last_forwarded_json['Functions']['tables']: + for sig in forwarded_json['Functions']['tables']: asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] @@ -679,7 +679,7 @@ def get_function_pointer_error(sig): pointer = ' \'" + x + "\' ' extra = ' Module["printErr"]("This pointer might make sense in another type signature: ' # sort signatures, attempting to show most likely related ones first - sigs = last_forwarded_json['Functions']['tables'].keys() + sigs = forwarded_json['Functions']['tables'].keys() def keyfunc(other): ret = 0 minlen = min(len(other), len(sig)) @@ -715,7 +715,7 @@ def keyfunc(other): basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_LOAD_D', 'SAFE_HEAP_STORE', 'SAFE_HEAP_STORE_D', 'SAFE_FT_MASK'] if settings['ASSERTIONS']: if settings['ASSERTIONS'] >= 2: import difflib - for sig in last_forwarded_json['Functions']['tables'].iterkeys(): + for sig in forwarded_json['Functions']['tables'].iterkeys(): basic_funcs += ['nullFunc_' + sig] asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig) + 'abort(x) }\n' @@ -754,7 +754,7 @@ def table_size(table): return 0 return table_contents.count(',') + 1 - table_total_size = sum(map(table_size, last_forwarded_json['Functions']['tables'].values())) + table_total_size = sum(map(table_size, forwarded_json['Functions']['tables'].values())) asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size if not settings['EMULATED_FUNCTION_POINTERS']: asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size @@ -779,12 +779,12 @@ def table_size(table): # function tables if not settings['EMULATED_FUNCTION_POINTERS']: - function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']] + function_tables = ['dynCall_' + table for table in forwarded_json['Functions']['tables']] else: function_tables = [] function_tables_impls = [] - for sig in last_forwarded_json['Functions']['tables'].iterkeys(): + for sig in forwarded_json['Functions']['tables'].iterkeys(): args = ','.join(['a' + str(i) for i in range(1, len(sig))]) arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) @@ -979,7 +979,7 @@ def add_simd_casts(t1, t2): receiving += ';\n' if settings['EXPORT_FUNCTION_TABLES'] and not settings['BINARYEN']: - for table in last_forwarded_json['Functions']['tables'].values(): + for table in forwarded_json['Functions']['tables'].values(): tableName = table.split()[1] table = table.replace('var ' + tableName, 'var ' + tableName + ' = Module["' + tableName + '"]') receiving += table + '\n' @@ -989,9 +989,9 @@ def add_simd_casts(t1, t2): final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs if settings.get('EMULATED_FUNCTION_POINTERS'): asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' - receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in last_forwarded_json['Functions']['tables']]) + receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in forwarded_json['Functions']['tables']]) if not settings['BINARYEN']: - for sig in last_forwarded_json['Functions']['tables'].keys(): + for sig in forwarded_json['Functions']['tables'].keys(): name = 'FUNCTION_TABLE_' + sig fullname = name if not settings['SIDE_MODULE'] else ('SIDE_' + name) receiving += 'Module["' + name + '"] = ' + fullname + ';\n' @@ -1001,9 +1001,10 @@ def add_simd_casts(t1, t2): if DEBUG: logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) - return post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, last_forwarded_json + return post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json -def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, last_forwarded_json, settings, outfile, DEBUG): + +def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG): if DEBUG: logging.debug('emscript: python processing: finalize') @@ -1377,7 +1378,7 @@ def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm # Set function table masks masks = {} max_mask = 0 - for sig, table in last_forwarded_json['Functions']['tables'].iteritems(): + for sig, table in forwarded_json['Functions']['tables'].iteritems(): mask = table.count(',') masks[sig] = str(mask) max_mask = max(mask, max_mask) @@ -1392,7 +1393,7 @@ def fix(m): if settings['SIDE_MODULE']: funcs_js.append(''' Runtime.registerFunctions(%(sigs)s, Module); -''' % { 'sigs': str(map(str, last_forwarded_json['Functions']['tables'].keys())) }) +''' % { 'sigs': str(map(str, forwarded_json['Functions']['tables'].keys())) }) for i in range(len(funcs_js)): # do this loop carefully to save memory if WINDOWS: funcs_js[i] = funcs_js[i].replace('\r\n', '\n') # Normalize to UNIX line endings, otherwise writing to text file will duplicate \r\n to \r\r\n! From 4d9be40ba45ec906ae546b840524210af4f5349a Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 15:54:46 -0700 Subject: [PATCH 07/52] extract memory_and_global_initializers --- emscripten.py | 187 +++++++++++++++++++++++++------------------------- 1 file changed, 95 insertions(+), 92 deletions(-) diff --git a/emscripten.py b/emscripten.py index abb9b6a5a688a..6d1c38e945534 100755 --- a/emscripten.py +++ b/emscripten.py @@ -357,98 +357,7 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, #print >> sys.stderr, 'glue:', pre, '\n\n||||||||||||||||\n\n', post, '...............' - # memory and global initializers - - global_initializers = str(', '.join(map(lambda i: '{ func: function() { %s() } }' % i, metadata['initializers']))) - - if settings['SIMD'] == 1: - pre = open(path_from_root(os.path.join('src', 'ecmascript_simd.js'))).read() + '\n\n' + pre - - staticbump = metadata['staticBump'] - while staticbump % 16 != 0: staticbump += 1 - pre = pre.replace('STATICTOP = STATIC_BASE + 0;', '''STATICTOP = STATIC_BASE + %d;%s - /* global initializers */ %s __ATINIT__.push(%s); - %s''' % (staticbump, - 'assert(STATICTOP < SPLIT_MEMORY, "SPLIT_MEMORY size must be big enough so the entire static memory, need " + STATICTOP);' if settings['SPLIT_MEMORY'] else '', - 'if (!ENVIRONMENT_IS_PTHREAD)' if settings['USE_PTHREADS'] else '', - global_initializers, - mem_init)) - - if settings['SIDE_MODULE']: - pre = pre.replace('Runtime.GLOBAL_BASE', 'gb') - if settings['SIDE_MODULE'] or settings['BINARYEN']: - pre = pre.replace('{{{ STATIC_BUMP }}}', str(staticbump)) - - funcs_js = [funcs] - parts = pre.split('// ASM_LIBRARY FUNCTIONS\n') - if len(parts) > 1: - pre = parts[0] - funcs_js.append(parts[1]) - - # merge forwarded data - settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] - all_exported_functions = set(shared.expand_response(settings['EXPORTED_FUNCTIONS'])) # both asm.js and otherwise - - for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented - all_exported_functions.add('_' + additional_export) - if settings['EXPORT_FUNCTION_TABLES']: - for table in forwarded_json['Functions']['tables'].values(): - for func in table.split('[')[1].split(']')[0].split(','): - if func[0] == '_': - all_exported_functions.add(func) - exported_implemented_functions = set(metadata['exports']) - export_bindings = settings['EXPORT_BINDINGS'] - export_all = settings['EXPORT_ALL'] - all_implemented = metadata['implementedFunctions'] + forwarded_json['Functions']['implementedFunctions'].keys() # XXX perf? - for key in all_implemented: - if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): - exported_implemented_functions.add(key) - implemented_functions = set(metadata['implementedFunctions']) - if settings['ASSERTIONS'] and settings.get('ORIGINAL_EXPORTED_FUNCTIONS'): - original_exports = settings['ORIGINAL_EXPORTED_FUNCTIONS'] - if original_exports[0] == '@': original_exports = json.loads(open(original_exports[1:]).read()) - for requested in original_exports: - # check if already implemented - # special-case malloc, EXPORTED by default for internal use, but we bake in a trivial allocator and warn at runtime if used in ASSERTIONS \ - if requested not in all_implemented and \ - requested != '_malloc' and \ - (('function ' + requested.encode('utf-8')) not in pre): # could be a js library func - logging.warning('function requested to be exported, but not implemented: "%s"', requested) - - if settings['BINARYEN'] and settings['SIDE_MODULE']: - assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)' - - asm_consts = [0]*len(metadata['asmConsts']) - all_sigs = [] - for k, v in metadata['asmConsts'].iteritems(): - const = v[0].encode('utf-8') - sigs = v[1] - if len(const) > 1 and const[0] == '"' and const[-1] == '"': - const = const[1:-1] - const = '{ ' + const + ' }' - args = [] - arity = max(map(len, sigs)) - 1 - for i in range(arity): - args.append('$' + str(i)) - const = 'function(' + ', '.join(args) + ') ' + const - asm_consts[int(k)] = const - all_sigs += sigs - - asm_const_funcs = [] - for sig in set(all_sigs): - forwarded_json['Functions']['libraryFunctions']['_emscripten_asm_const_' + sig] = 1 - args = ['a%d' % i for i in range(len(sig)-1)] - all_args = ['code'] + args - asm_const_funcs.append(r''' -function _emscripten_asm_const_%s(%s) { - return ASM_CONSTS[code](%s); -}''' % (sig.encode('utf-8'), ', '.join(all_args), ', '.join(args))) - - pre = pre.replace('// === Body ===', '// === Body ===\n' + '\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') - - #if DEBUG: outfile.write('// pre\n') - outfile.write(pre) - pre = None + funcs_js, implemented_functions, exported_implemented_functions = memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_json, settings, outfile, DEBUG) #if DEBUG: outfile.write('// funcs\n') @@ -1004,6 +913,100 @@ def add_simd_casts(t1, t2): return post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json +def memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_json, settings, outfile, DEBUG): + global_initializers = str(', '.join(map(lambda i: '{ func: function() { %s() } }' % i, metadata['initializers']))) + + if settings['SIMD'] == 1: + pre = open(path_from_root(os.path.join('src', 'ecmascript_simd.js'))).read() + '\n\n' + pre + + staticbump = metadata['staticBump'] + while staticbump % 16 != 0: staticbump += 1 + pre = pre.replace('STATICTOP = STATIC_BASE + 0;', '''STATICTOP = STATIC_BASE + %d;%s +/* global initializers */ %s __ATINIT__.push(%s); +%s''' % (staticbump, + 'assert(STATICTOP < SPLIT_MEMORY, "SPLIT_MEMORY size must be big enough so the entire static memory, need " + STATICTOP);' if settings['SPLIT_MEMORY'] else '', + 'if (!ENVIRONMENT_IS_PTHREAD)' if settings['USE_PTHREADS'] else '', + global_initializers, + mem_init)) + + if settings['SIDE_MODULE']: + pre = pre.replace('Runtime.GLOBAL_BASE', 'gb') + if settings['SIDE_MODULE'] or settings['BINARYEN']: + pre = pre.replace('{{{ STATIC_BUMP }}}', str(staticbump)) + + funcs_js = [funcs] + parts = pre.split('// ASM_LIBRARY FUNCTIONS\n') + if len(parts) > 1: + pre = parts[0] + funcs_js.append(parts[1]) + + # merge forwarded data + settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] + all_exported_functions = set(shared.expand_response(settings['EXPORTED_FUNCTIONS'])) # both asm.js and otherwise + + for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented + all_exported_functions.add('_' + additional_export) + if settings['EXPORT_FUNCTION_TABLES']: + for table in forwarded_json['Functions']['tables'].values(): + for func in table.split('[')[1].split(']')[0].split(','): + if func[0] == '_': + all_exported_functions.add(func) + exported_implemented_functions = set(metadata['exports']) + export_bindings = settings['EXPORT_BINDINGS'] + export_all = settings['EXPORT_ALL'] + all_implemented = metadata['implementedFunctions'] + forwarded_json['Functions']['implementedFunctions'].keys() # XXX perf? + for key in all_implemented: + if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): + exported_implemented_functions.add(key) + implemented_functions = set(metadata['implementedFunctions']) + if settings['ASSERTIONS'] and settings.get('ORIGINAL_EXPORTED_FUNCTIONS'): + original_exports = settings['ORIGINAL_EXPORTED_FUNCTIONS'] + if original_exports[0] == '@': original_exports = json.loads(open(original_exports[1:]).read()) + for requested in original_exports: + # check if already implemented + # special-case malloc, EXPORTED by default for internal use, but we bake in a trivial allocator and warn at runtime if used in ASSERTIONS \ + if requested not in all_implemented and \ + requested != '_malloc' and \ + (('function ' + requested.encode('utf-8')) not in pre): # could be a js library func + logging.warning('function requested to be exported, but not implemented: "%s"', requested) + + if settings['BINARYEN'] and settings['SIDE_MODULE']: + assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)' + + asm_consts = [0]*len(metadata['asmConsts']) + all_sigs = [] + for k, v in metadata['asmConsts'].iteritems(): + const = v[0].encode('utf-8') + sigs = v[1] + if len(const) > 1 and const[0] == '"' and const[-1] == '"': + const = const[1:-1] + const = '{ ' + const + ' }' + args = [] + arity = max(map(len, sigs)) - 1 + for i in range(arity): + args.append('$' + str(i)) + const = 'function(' + ', '.join(args) + ') ' + const + asm_consts[int(k)] = const + all_sigs += sigs + + asm_const_funcs = [] + for sig in set(all_sigs): + forwarded_json['Functions']['libraryFunctions']['_emscripten_asm_const_' + sig] = 1 + args = ['a%d' % i for i in range(len(sig)-1)] + all_args = ['code'] + args + asm_const_funcs.append(r''' +function _emscripten_asm_const_%s(%s) { + return ASM_CONSTS[code](%s); +}''' % (sig.encode('utf-8'), ', '.join(all_args), ', '.join(args))) + + pre = pre.replace('// === Body ===', '// === Body ===\n' + '\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') + + #if DEBUG: outfile.write('// pre\n') + outfile.write(pre) + + return funcs_js, implemented_functions, exported_implemented_functions + + def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG): if DEBUG: From f2599af5f3d693204a207263dc1ec0b8b344a941 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 16:44:37 -0700 Subject: [PATCH 08/52] Further splitting of pre-EMSCRIPTEN_END_FUNCS code --- emscripten.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/emscripten.py b/emscripten.py index 6d1c38e945534..4017dfd79b9e0 100755 --- a/emscripten.py +++ b/emscripten.py @@ -357,7 +357,16 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, #print >> sys.stderr, 'glue:', pre, '\n\n||||||||||||||||\n\n', post, '...............' - funcs_js, implemented_functions, exported_implemented_functions = memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_json, settings, outfile, DEBUG) + pre = memory_and_global_initializers(pre, metadata, mem_init, settings) + pre, funcs_js = get_js_funcs(pre, funcs) + exported_implemented_functions, all_implemented = get_exported_implemented_functions(metadata, forwarded_json, settings) + implemented_functions = get_implemented_functions(pre, metadata, forwarded_json, settings, all_implemented) + if settings['BINARYEN'] and settings['SIDE_MODULE']: + assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)' + pre = include_asm_consts(pre, metadata, forwarded_json) + #if DEBUG: outfile.write('// pre\n') + outfile.write(pre) + pre = None #if DEBUG: outfile.write('// funcs\n') @@ -913,7 +922,7 @@ def add_simd_casts(t1, t2): return post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json -def memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_json, settings, outfile, DEBUG): +def memory_and_global_initializers(pre, metadata, mem_init, settings): global_initializers = str(', '.join(map(lambda i: '{ func: function() { %s() } }' % i, metadata['initializers']))) if settings['SIMD'] == 1: @@ -934,12 +943,19 @@ def memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_jso if settings['SIDE_MODULE'] or settings['BINARYEN']: pre = pre.replace('{{{ STATIC_BUMP }}}', str(staticbump)) + return pre + + +def get_js_funcs(pre, funcs): funcs_js = [funcs] parts = pre.split('// ASM_LIBRARY FUNCTIONS\n') if len(parts) > 1: pre = parts[0] funcs_js.append(parts[1]) + return pre, funcs_js + +def get_exported_implemented_functions(metadata, forwarded_json, settings): # merge forwarded data settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] all_exported_functions = set(shared.expand_response(settings['EXPORTED_FUNCTIONS'])) # both asm.js and otherwise @@ -958,6 +974,10 @@ def memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_jso for key in all_implemented: if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): exported_implemented_functions.add(key) + return exported_implemented_functions, all_implemented + + +def get_implemented_functions(pre, metadata, forwarded_json, settings, all_implemented): implemented_functions = set(metadata['implementedFunctions']) if settings['ASSERTIONS'] and settings.get('ORIGINAL_EXPORTED_FUNCTIONS'): original_exports = settings['ORIGINAL_EXPORTED_FUNCTIONS'] @@ -970,9 +990,10 @@ def memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_jso (('function ' + requested.encode('utf-8')) not in pre): # could be a js library func logging.warning('function requested to be exported, but not implemented: "%s"', requested) - if settings['BINARYEN'] and settings['SIDE_MODULE']: - assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)' + return implemented_functions + +def include_asm_consts(pre, metadata, forwarded_json): asm_consts = [0]*len(metadata['asmConsts']) all_sigs = [] for k, v in metadata['asmConsts'].iteritems(): @@ -999,12 +1020,7 @@ def memory_and_global_initializers(pre, funcs, metadata, mem_init, forwarded_jso return ASM_CONSTS[code](%s); }''' % (sig.encode('utf-8'), ', '.join(all_args), ', '.join(args))) - pre = pre.replace('// === Body ===', '// === Body ===\n' + '\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') - - #if DEBUG: outfile.write('// pre\n') - outfile.write(pre) - - return funcs_js, implemented_functions, exported_implemented_functions + return pre.replace('// === Body ===', '// === Body ===\n\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG): From 6db1dcbbc424f85ec9bf6e224525a2ff63d60a33 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Mon, 10 Apr 2017 17:20:07 -0700 Subject: [PATCH 09/52] Move block of simd type generation out --- emscripten.py | 120 ++++++++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 52 deletions(-) diff --git a/emscripten.py b/emscripten.py index 4017dfd79b9e0..cd014ad60186a 100755 --- a/emscripten.py +++ b/emscripten.py @@ -528,54 +528,7 @@ def make_emulated_param(i): asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] - simdfloattypes = [] - simdinttypes = [] - simdbooltypes = [] - simdfuncs = ['splat', 'check', 'extractLane', 'replaceLane'] - simdintfloatfuncs = ['add', 'sub', 'neg', 'mul', - 'equal', 'lessThan', 'greaterThan', - 'notEqual', 'lessThanOrEqual', 'greaterThanOrEqual', - 'select', 'swizzle', 'shuffle', - 'load', 'store', 'load1', 'store1', 'load2', 'store2'] - simdintboolfuncs = ['and', 'xor', 'or', 'not'] - if metadata['simdUint8x16']: - simdinttypes += ['Uint8x16'] - simdintfloatfuncs += ['fromUint8x16Bits'] - if metadata['simdInt8x16']: - simdinttypes += ['Int8x16'] - simdintfloatfuncs += ['fromInt8x16Bits'] - if metadata['simdUint16x8']: - simdinttypes += ['Uint16x8'] - simdintfloatfuncs += ['fromUint16x8Bits'] - if metadata['simdInt16x8']: - simdinttypes += ['Int16x8'] - simdintfloatfuncs += ['fromInt16x8Bits'] - if metadata['simdUint32x4']: - simdinttypes += ['Uint32x4'] - simdintfloatfuncs += ['fromUint32x4Bits'] - if metadata['simdInt32x4'] or settings['SIMD']: # Always import Int32x4 when building with -s SIMD=1, since memcpy is SIMD optimized. - simdinttypes += ['Int32x4'] - simdintfloatfuncs += ['fromInt32x4Bits'] - if metadata['simdFloat32x4']: - simdfloattypes += ['Float32x4'] - simdintfloatfuncs += ['fromFloat32x4Bits'] - if metadata['simdFloat64x2']: - simdfloattypes += ['Float64x2'] - simdintfloatfuncs += ['fromFloat64x2Bits'] - if metadata['simdBool8x16']: - simdbooltypes += ['Bool8x16'] - if metadata['simdBool16x8']: - simdbooltypes += ['Bool16x8'] - if metadata['simdBool32x4']: - simdbooltypes += ['Bool32x4'] - if metadata['simdBool64x2']: - simdbooltypes += ['Bool64x2'] - - simdfloatfuncs = simdfuncs + simdintfloatfuncs + ['div', 'min', 'max', 'minNum', 'maxNum', 'sqrt', - 'abs', 'reciprocalApproximation', 'reciprocalSqrtApproximation'] - simdintfuncs = simdfuncs + simdintfloatfuncs + simdintboolfuncs + ['shiftLeftByScalar', 'shiftRightByScalar', 'addSaturate', 'subSaturate'] - simdboolfuncs = simdfuncs + simdintboolfuncs + ['anyTrue', 'allTrue'] - simdtypes = simdfloattypes + simdinttypes + simdbooltypes + simd = make_simd_types(metadata, settings) fundamentals = ['Math'] fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] @@ -834,17 +787,17 @@ def string_contains_any(s, str_list): nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8', 'Float64x2'] for y in ['load2', 'store2']] nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8'] for y in ['load1', 'store1']] - asm_global_funcs += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simdtypes]) + asm_global_funcs += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simd['types']]) - simd_int_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simdinttypes for g in simdintfuncs] + simd_int_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['int_types'] for g in simd['int_funcs']] simd_int_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_int_symbols) asm_global_funcs += ''.join(simd_int_symbols) - simd_float_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simdfloattypes for g in simdfloatfuncs] + simd_float_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['float_types'] for g in simd['float_funcs']] simd_float_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_float_symbols) asm_global_funcs += ''.join(simd_float_symbols) - simd_bool_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simdbooltypes for g in simdboolfuncs] + simd_bool_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['bool_types'] for g in simd['bool_funcs']] simd_bool_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_bool_symbols) asm_global_funcs += ''.join(simd_bool_symbols) @@ -1023,6 +976,69 @@ def include_asm_consts(pre, metadata, forwarded_json): return pre.replace('// === Body ===', '// === Body ===\n\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') +def make_simd_types(metadata, settings): + simd_float_types = [] + simd_int_types = [] + simd_bool_types = [] + simd_funcs = ['splat', 'check', 'extractLane', 'replaceLane'] + simd_intfloat_funcs = ['add', 'sub', 'neg', 'mul', + 'equal', 'lessThan', 'greaterThan', + 'notEqual', 'lessThanOrEqual', 'greaterThanOrEqual', + 'select', 'swizzle', 'shuffle', + 'load', 'store', 'load1', 'store1', 'load2', 'store2'] + simd_intbool_funcs = ['and', 'xor', 'or', 'not'] + if metadata['simdUint8x16']: + simd_int_types += ['Uint8x16'] + simd_intfloat_funcs += ['fromUint8x16Bits'] + if metadata['simdInt8x16']: + simd_int_types += ['Int8x16'] + simd_intfloat_funcs += ['fromInt8x16Bits'] + if metadata['simdUint16x8']: + simd_int_types += ['Uint16x8'] + simd_intfloat_funcs += ['fromUint16x8Bits'] + if metadata['simdInt16x8']: + simd_int_types += ['Int16x8'] + simd_intfloat_funcs += ['fromInt16x8Bits'] + if metadata['simdUint32x4']: + simd_int_types += ['Uint32x4'] + simd_intfloat_funcs += ['fromUint32x4Bits'] + if metadata['simdInt32x4'] or settings['SIMD']: + # Always import Int32x4 when building with -s SIMD=1, since memcpy is SIMD optimized. + simd_int_types += ['Int32x4'] + simd_intfloat_funcs += ['fromInt32x4Bits'] + if metadata['simdFloat32x4']: + simd_float_types += ['Float32x4'] + simd_intfloat_funcs += ['fromFloat32x4Bits'] + if metadata['simdFloat64x2']: + simd_float_types += ['Float64x2'] + simd_intfloat_funcs += ['fromFloat64x2Bits'] + if metadata['simdBool8x16']: + simd_bool_types += ['Bool8x16'] + if metadata['simdBool16x8']: + simd_bool_types += ['Bool16x8'] + if metadata['simdBool32x4']: + simd_bool_types += ['Bool32x4'] + if metadata['simdBool64x2']: + simd_bool_types += ['Bool64x2'] + + simd_float_funcs = simd_funcs + simd_intfloat_funcs + ['div', 'min', 'max', 'minNum', 'maxNum', 'sqrt', + 'abs', 'reciprocalApproximation', 'reciprocalSqrtApproximation'] + simd_int_funcs = simd_funcs + simd_intfloat_funcs + simd_intbool_funcs + ['shiftLeftByScalar', 'shiftRightByScalar', 'addSaturate', 'subSaturate'] + simd_bool_funcs = simd_funcs + simd_intbool_funcs + ['anyTrue', 'allTrue'] + simd_types = simd_float_types + simd_int_types + simd_bool_types + return { + 'types': simd_types, + 'float_types': simd_float_types, + 'int_types': simd_int_types, + 'bool_types': simd_bool_types, + 'funcs': simd_funcs, + 'float_funcs': simd_float_funcs, + 'int_funcs': simd_int_funcs, + 'bool_funcs': simd_bool_funcs, + 'intfloat_funcs': simd_intfloat_funcs, + 'intbool_funcs': simd_intbool_funcs, + } + def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG): if DEBUG: From f25f0c90a9b992dc5c29eb619f5f072816543762 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 10:30:45 -0700 Subject: [PATCH 10/52] Move all simd function generation into global_simd_funcs --- emscripten.py | 105 ++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/emscripten.py b/emscripten.py index cd014ad60186a..0315c909d8433 100755 --- a/emscripten.py +++ b/emscripten.py @@ -528,7 +528,6 @@ def make_emulated_param(i): asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] - simd = make_simd_types(metadata, settings) fundamentals = ['Math'] fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] @@ -776,51 +775,9 @@ def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1] asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global' + access_quote(g) + ';\n' for g in maths]) asm_global_funcs += ''.join([' var ' + g + '=env' + access_quote(math_fix(g)) + ';\n' for g in basic_funcs + global_funcs]) - if metadata['simd'] or settings['SIMD']: # Always import SIMD when building with -s SIMD=1, since in that mode memcpy is SIMD optimized. - def string_contains_any(s, str_list): - for sub in str_list: - if sub in s: - return True - return False - nonexisting_simd_symbols = ['Int8x16_fromInt8x16', 'Uint8x16_fromUint8x16', 'Int16x8_fromInt16x8', 'Uint16x8_fromUint16x8', 'Int32x4_fromInt32x4', 'Uint32x4_fromUint32x4', 'Float32x4_fromFloat32x4', 'Float64x2_fromFloat64x2'] - nonexisting_simd_symbols += ['Int32x4_addSaturate', 'Int32x4_subSaturate', 'Uint32x4_addSaturate', 'Uint32x4_subSaturate'] - nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8', 'Float64x2'] for y in ['load2', 'store2']] - nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8'] for y in ['load1', 'store1']] - - asm_global_funcs += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simd['types']]) - - simd_int_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['int_types'] for g in simd['int_funcs']] - simd_int_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_int_symbols) - asm_global_funcs += ''.join(simd_int_symbols) - - simd_float_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['float_types'] for g in simd['float_funcs']] - simd_float_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_float_symbols) - asm_global_funcs += ''.join(simd_float_symbols) - - simd_bool_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['bool_types'] for g in simd['bool_funcs']] - simd_bool_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_bool_symbols) - asm_global_funcs += ''.join(simd_bool_symbols) - - # SIMD conversions (not bitcasts) between same lane sizes: - def add_simd_cast(dst, src): - return ' var SIMD_' + dst + '_from' + src + '=SIMD_' + dst + '.from' + src + ';\n' - def add_simd_casts(t1, t2): - return add_simd_cast(t1, t2) + add_simd_cast(t2, t1) - - # Bug: Skip importing conversions for int<->uint for now, they don't validate as asm.js. https://bugzilla.mozilla.org/show_bug.cgi?id=1313512 - # This is not an issue when building SSEx code, because it doesn't use these. (but it will be an issue if using SIMD.js intrinsics from vector.h to explicitly call these) -# if metadata['simdInt8x16'] and metadata['simdUint8x16']: asm_global_funcs += add_simd_casts('Int8x16', 'Uint8x16') -# if metadata['simdInt16x8'] and metadata['simdUint16x8']: asm_global_funcs += add_simd_casts('Int16x8', 'Uint16x8') -# if metadata['simdInt32x4'] and metadata['simdUint32x4']: asm_global_funcs += add_simd_casts('Int32x4', 'Uint32x4') - - if metadata['simdInt32x4'] and metadata['simdFloat32x4']: asm_global_funcs += add_simd_casts('Int32x4', 'Float32x4') - if metadata['simdUint32x4'] and metadata['simdFloat32x4']: asm_global_funcs += add_simd_casts('Uint32x4', 'Float32x4') - if metadata['simdInt32x4'] and metadata['simdFloat64x2']: asm_global_funcs += add_simd_cast('Int32x4', 'Float64x2') # Unofficial, needed for emscripten_int32x4_fromFloat64x2 - if metadata['simdUint32x4'] and metadata['simdFloat64x2']: asm_global_funcs += add_simd_cast('Uint32x4', 'Float64x2') # Unofficial, needed for emscripten_uint32x4_fromFloat64x2 - - # Unofficial, Bool64x2 does not yet exist, but needed for Float64x2 comparisons. - if metadata['simdFloat64x2']: - asm_global_funcs += ' var SIMD_Int32x4_fromBool64x2Bits = global.SIMD.Int32x4.fromBool64x2Bits;\n' + + asm_global_funcs += global_simd_funcs(access_quote, metadata, settings) + if settings['USE_PTHREADS']: asm_global_funcs += ''.join([' var Atomics_' + ty + '=global' + access_quote('Atomics') + access_quote(ty) + ';\n' for ty in ['load', 'store', 'exchange', 'compareExchange', 'add', 'sub', 'and', 'or', 'xor']]) asm_global_vars = ''.join([' var ' + g + '=env' + access_quote(g) + '|0;\n' for g in basic_vars + global_vars]) @@ -976,6 +933,62 @@ def include_asm_consts(pre, metadata, forwarded_json): return pre.replace('// === Body ===', '// === Body ===\n\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') +def global_simd_funcs(access_quote, metadata, settings): + # Always import SIMD when building with -s SIMD=1, since in that mode memcpy is SIMD optimized. + if not (metadata['simd'] or settings['SIMD']): + return '' + + def string_contains_any(s, str_list): + for sub in str_list: + if sub in s: + return True + return False + + simd = make_simd_types(metadata, settings) + + nonexisting_simd_symbols = ['Int8x16_fromInt8x16', 'Uint8x16_fromUint8x16', 'Int16x8_fromInt16x8', 'Uint16x8_fromUint16x8', 'Int32x4_fromInt32x4', 'Uint32x4_fromUint32x4', 'Float32x4_fromFloat32x4', 'Float64x2_fromFloat64x2'] + nonexisting_simd_symbols += ['Int32x4_addSaturate', 'Int32x4_subSaturate', 'Uint32x4_addSaturate', 'Uint32x4_subSaturate'] + nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8', 'Float64x2'] for y in ['load2', 'store2']] + nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8'] for y in ['load1', 'store1']] + + func_js = '' + func_js += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simd['types']]) + + simd_int_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['int_types'] for g in simd['int_funcs']] + simd_int_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_int_symbols) + func_js += ''.join(simd_int_symbols) + + simd_float_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['float_types'] for g in simd['float_funcs']] + simd_float_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_float_symbols) + func_js += ''.join(simd_float_symbols) + + simd_bool_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['bool_types'] for g in simd['bool_funcs']] + simd_bool_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_bool_symbols) + func_js += ''.join(simd_bool_symbols) + + # SIMD conversions (not bitcasts) between same lane sizes: + def add_simd_cast(dst, src): + return ' var SIMD_' + dst + '_from' + src + '=SIMD_' + dst + '.from' + src + ';\n' + def add_simd_casts(t1, t2): + return add_simd_cast(t1, t2) + add_simd_cast(t2, t1) + + # Bug: Skip importing conversions for int<->uint for now, they don't validate as asm.js. https://bugzilla.mozilla.org/show_bug.cgi?id=1313512 + # This is not an issue when building SSEx code, because it doesn't use these. (but it will be an issue if using SIMD.js intrinsics from vector.h to explicitly call these) +# if metadata['simdInt8x16'] and metadata['simdUint8x16']: func_js += add_simd_casts('Int8x16', 'Uint8x16') +# if metadata['simdInt16x8'] and metadata['simdUint16x8']: func_js += add_simd_casts('Int16x8', 'Uint16x8') +# if metadata['simdInt32x4'] and metadata['simdUint32x4']: func_js += add_simd_casts('Int32x4', 'Uint32x4') + + if metadata['simdInt32x4'] and metadata['simdFloat32x4']: func_js += add_simd_casts('Int32x4', 'Float32x4') + if metadata['simdUint32x4'] and metadata['simdFloat32x4']: func_js += add_simd_casts('Uint32x4', 'Float32x4') + if metadata['simdInt32x4'] and metadata['simdFloat64x2']: func_js += add_simd_cast('Int32x4', 'Float64x2') # Unofficial, needed for emscripten_int32x4_fromFloat64x2 + if metadata['simdUint32x4'] and metadata['simdFloat64x2']: func_js += add_simd_cast('Uint32x4', 'Float64x2') # Unofficial, needed for emscripten_uint32x4_fromFloat64x2 + + # Unofficial, Bool64x2 does not yet exist, but needed for Float64x2 comparisons. + if metadata['simdFloat64x2']: + func_js += ' var SIMD_Int32x4_fromBool64x2Bits = global.SIMD.Int32x4.fromBool64x2Bits;\n' + return func_js + + def make_simd_types(metadata, settings): simd_float_types = [] simd_int_types = [] From aabd34a835c002b7827413cd870fb74ceeafb1ab Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 10:58:19 -0700 Subject: [PATCH 11/52] reduce duplication in global_simd_funcs --- emscripten.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/emscripten.py b/emscripten.py index 0315c909d8433..4dec2f113c39a 100755 --- a/emscripten.py +++ b/emscripten.py @@ -944,27 +944,24 @@ def string_contains_any(s, str_list): return True return False - simd = make_simd_types(metadata, settings) - nonexisting_simd_symbols = ['Int8x16_fromInt8x16', 'Uint8x16_fromUint8x16', 'Int16x8_fromInt16x8', 'Uint16x8_fromUint16x8', 'Int32x4_fromInt32x4', 'Uint32x4_fromUint32x4', 'Float32x4_fromFloat32x4', 'Float64x2_fromFloat64x2'] nonexisting_simd_symbols += ['Int32x4_addSaturate', 'Int32x4_subSaturate', 'Uint32x4_addSaturate', 'Uint32x4_subSaturate'] nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8', 'Float64x2'] for y in ['load2', 'store2']] nonexisting_simd_symbols += [(x + '_' + y) for x in ['Int8x16', 'Uint8x16', 'Int16x8', 'Uint16x8'] for y in ['load1', 'store1']] + simd = make_simd_types(metadata, settings) + func_js = '' func_js += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simd['types']]) - simd_int_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['int_types'] for g in simd['int_funcs']] - simd_int_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_int_symbols) - func_js += ''.join(simd_int_symbols) - - simd_float_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['float_types'] for g in simd['float_funcs']] - simd_float_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_float_symbols) - func_js += ''.join(simd_float_symbols) + def generate_symbols(types, funcs): + symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in types for g in funcs] + symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), symbols) + return ''.join(symbols) - simd_bool_symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simd['bool_types'] for g in simd['bool_funcs']] - simd_bool_symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), simd_bool_symbols) - func_js += ''.join(simd_bool_symbols) + func_js += generate_symbols(simd['int_types'], simd['int_funcs']) + func_js += generate_symbols(simd['float_types'], simd['float_funcs']) + func_js += generate_symbols(simd['bool_types'], simd['bool_funcs']) # SIMD conversions (not bitcasts) between same lane sizes: def add_simd_cast(dst, src): From b36a28e29abca2cbff89bfe25a2376270fc24050 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 12:36:37 -0700 Subject: [PATCH 12/52] Extract creating function tables --- emscripten.py | 278 ++++++++++++++++++++++++++------------------------ 1 file changed, 144 insertions(+), 134 deletions(-) diff --git a/emscripten.py b/emscripten.py index 4dec2f113c39a..47c7fca166180 100755 --- a/emscripten.py +++ b/emscripten.py @@ -370,19 +370,6 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, #if DEBUG: outfile.write('// funcs\n') - # when emulating function pointer casts, we need to know what is the target of each pointer - if settings['EMULATE_FUNCTION_POINTER_CASTS']: - function_pointer_targets = {} - for sig, table in forwarded_json['Functions']['tables'].iteritems(): - start = table.index('[') - end = table.rindex(']') - body = table[start+1:end].split(',') - parsed = map(lambda x: x.strip(), body) - for i in range(len(parsed)): - if parsed[i] != '0': - assert i not in function_pointer_targets - function_pointer_targets[i] = [sig, str(parsed[i])] - # Move preAsms to their right place def move_preasm(m): contents = m.groups(0)[0] @@ -391,135 +378,16 @@ def move_preasm(m): if not settings['BOOTSTRAPPING_STRUCT_INFO'] and len(funcs_js) > 1: funcs_js[1] = re.sub(r'/\* PRE_ASM \*/(.*)\n', move_preasm, funcs_js[1]) - class Counter: - i = 0 - j = 0 if 'pre' in forwarded_json['Functions']['tables']: pre_tables = forwarded_json['Functions']['tables']['pre'] del forwarded_json['Functions']['tables']['pre'] else: pre_tables = '' - def unfloat(s): - return 'd' if s == 'f' else s # lower float to double for ffis - - if settings['ASSERTIONS'] >= 2: - debug_tables = {} - - def make_params(sig): return ','.join(['p%d' % p for p in range(len(sig)-1)]) - def make_coerced_params(sig): return ','.join([shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) - def make_coercions(sig): return ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';' - def make_func(name, code, params, coercions): return 'function %s(%s) {\n %s %s\n}' % (name, params, coercions, code) - in_table = set() + debug_tables = {} - def make_table(sig, raw): - if '[]' in raw: return ('', '') # empty table - params = make_params(sig) - coerced_params = make_coerced_params(sig) - coercions = make_coercions(sig) - def make_bad(target=None): - i = Counter.i - Counter.i += 1 - if target is None: target = i - name = 'b' + str(i) - if not settings['ASSERTIONS']: - code = 'abort(%s);' % target - else: - code = 'nullFunc_' + sig + '(%d);' % target - if sig[0] != 'v': - code += 'return %s' % shared.JS.make_initializer(sig[0], settings) + ';' - return name, make_func(name, code, params, coercions) - bad, bad_func = make_bad() # the default bad func - if settings['ASSERTIONS'] <= 1: - Counter.pre = [bad_func] - else: - Counter.pre = [] - start = raw.index('[') - end = raw.rindex(']') - body = raw[start+1:end].split(',') - if settings['EMULATED_FUNCTION_POINTERS']: - def receive(item): - if item == '0': - return item - else: - if item in all_implemented: - in_table.add(item) - return "asm['" + item + "']" - else: - return item # this is not implemented; it would normally be wrapped, but with emulation, we just use it directly outside - body = map(receive, body) - for j in range(settings['RESERVED_FUNCTION_POINTERS']): - curr = 'jsCall_%s_%s' % (sig, j) - body[settings['FUNCTION_POINTER_ALIGNMENT'] * (1 + j)] = curr - implemented_functions.add(curr) - Counter.j = 0 - def fix_item(item): - j = Counter.j - Counter.j += 1 - newline = Counter.j % 30 == 29 - if item == '0': - if j > 0 and settings['EMULATE_FUNCTION_POINTER_CASTS'] and j in function_pointer_targets: # emulate all non-null pointer calls, if asked to - proper_sig, proper_target = function_pointer_targets[j] - if settings['EMULATED_FUNCTION_POINTERS']: - if proper_target in all_implemented: - proper_target = "asm['" + proper_target + "']" - def make_emulated_param(i): - if i >= len(sig): return shared.JS.make_initializer(proper_sig[i], settings) # extra param, just send a zero - return shared.JS.make_coercion('p%d' % (i-1), proper_sig[i], settings, convert_from=sig[i]) - proper_code = proper_target + '(' + ','.join(map(lambda i: make_emulated_param(i+1), range(len(proper_sig)-1))) + ')' - if proper_sig[0] != 'v': - # proper sig has a return, which the wrapper may or may not use - proper_code = shared.JS.make_coercion(proper_code, proper_sig[0], settings) - if proper_sig[0] != sig[0]: - # first coercion ensured we call the target ok; this one ensures we return the right type in the wrapper - proper_code = shared.JS.make_coercion(proper_code, sig[0], settings, convert_from=proper_sig[0]) - if sig[0] != 'v': - proper_code = 'return ' + proper_code - else: - # proper sig has no return, we may need a fake return - if sig[0] != 'v': - proper_code = 'return ' + shared.JS.make_initializer(sig[0], settings) - name = 'fpemu_%s_%d' % (sig, j) - wrapper = make_func(name, proper_code, params, coercions) - Counter.pre.append(wrapper) - return name if not newline else (name + '\n') - if settings['ASSERTIONS'] <= 1: - return bad if not newline else (bad + '\n') - else: - specific_bad, specific_bad_func = make_bad(j) - Counter.pre.append(specific_bad_func) - return specific_bad if not newline else (specific_bad + '\n') - clean_item = item.replace("asm['", '').replace("']", '') - # when emulating function pointers, we don't need wrappers - # but if relocating, then we also have the copies in-module, and do - # in wasm we never need wrappers though - if clean_item not in implemented_functions and not (settings['EMULATED_FUNCTION_POINTERS'] and not settings['RELOCATABLE']) and not settings['BINARYEN']: - # this is imported into asm, we must wrap it - call_ident = clean_item - if call_ident in metadata['redirects']: call_ident = metadata['redirects'][call_ident] - if not call_ident.startswith('_') and not call_ident.startswith('Math_'): call_ident = '_' + call_ident - code = call_ident + '(' + coerced_params + ')' - if sig[0] != 'v': - # ffis cannot return float - if sig[0] == 'f': code = '+' + code - code = 'return ' + shared.JS.make_coercion(code, sig[0], settings) - code += ';' - Counter.pre.append(make_func(clean_item + '__wrapper', code, params, coercions)) - assert not sig == 'X', 'must know the signature in order to create a wrapper for "%s" (TODO for shared wasm modules)' % item - return clean_item + '__wrapper' - return item if not newline else (item + '\n') - if settings['ASSERTIONS'] >= 2: - debug_tables[sig] = body - body = ','.join(map(fix_item, body)) - return ('\n'.join(Counter.pre), ''.join([raw[:start+1], body, raw[end:]])) - - infos = [make_table(sig, raw) for sig, raw in forwarded_json['Functions']['tables'].iteritems()] - Counter.pre = [] - - function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n' - function_tables_defs += '\n// EMSCRIPTEN_END_FUNCS\n' - function_tables_defs += '\n'.join([info[1] for info in infos]) + function_tables_defs = make_function_tables_defs(in_table, debug_tables, implemented_functions, all_implemented, forwarded_json, settings, metadata) asm_setup = '' @@ -933,6 +801,148 @@ def include_asm_consts(pre, metadata, forwarded_json): return pre.replace('// === Body ===', '// === Body ===\n\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') +def unfloat(s): + """lower float to double for ffis""" + return 'd' if s == 'f' else s + + +class Counter: + i = 0 + j = 0 + + +def make_function_tables_defs(in_table, debug_tables, implemented_functions, all_implemented, forwarded_json, settings, metadata): + def make_params(sig): return ','.join(['p%d' % p for p in range(len(sig)-1)]) + def make_coerced_params(sig): return ','.join([shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) + def make_coercions(sig): return ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';' + + # when emulating function pointer casts, we need to know what is the target of each pointer + if settings['EMULATE_FUNCTION_POINTER_CASTS']: + function_pointer_targets = {} + for sig, table in forwarded_json['Functions']['tables'].iteritems(): + start = table.index('[') + end = table.rindex(']') + body = table[start+1:end].split(',') + parsed = map(lambda x: x.strip(), body) + for i in range(len(parsed)): + if parsed[i] != '0': + assert i not in function_pointer_targets + function_pointer_targets[i] = [sig, str(parsed[i])] + + def make_table(sig, raw): + if '[]' in raw: return ('', '') # empty table + params = make_params(sig) + coerced_params = make_coerced_params(sig) + coercions = make_coercions(sig) + def make_bad(target=None): + i = Counter.i + Counter.i += 1 + if target is None: target = i + name = 'b' + str(i) + if not settings['ASSERTIONS']: + code = 'abort(%s);' % target + else: + code = 'nullFunc_' + sig + '(%d);' % target + if sig[0] != 'v': + code += 'return %s' % shared.JS.make_initializer(sig[0], settings) + ';' + return name, make_func(name, code, params, coercions) + bad, bad_func = make_bad() # the default bad func + if settings['ASSERTIONS'] <= 1: + Counter.pre = [bad_func] + else: + Counter.pre = [] + start = raw.index('[') + end = raw.rindex(']') + body = raw[start+1:end].split(',') + if settings['EMULATED_FUNCTION_POINTERS']: + def receive(item): + if item == '0': + return item + else: + if item in all_implemented: + in_table.add(item) + return "asm['" + item + "']" + else: + return item # this is not implemented; it would normally be wrapped, but with emulation, we just use it directly outside + body = map(receive, body) + for j in range(settings['RESERVED_FUNCTION_POINTERS']): + curr = 'jsCall_%s_%s' % (sig, j) + body[settings['FUNCTION_POINTER_ALIGNMENT'] * (1 + j)] = curr + implemented_functions.add(curr) + Counter.j = 0 + def fix_item(item): + j = Counter.j + Counter.j += 1 + newline = Counter.j % 30 == 29 + if item == '0': + if j > 0 and settings['EMULATE_FUNCTION_POINTER_CASTS'] and j in function_pointer_targets: # emulate all non-null pointer calls, if asked to + proper_sig, proper_target = function_pointer_targets[j] + if settings['EMULATED_FUNCTION_POINTERS']: + if proper_target in all_implemented: + proper_target = "asm['" + proper_target + "']" + def make_emulated_param(i): + if i >= len(sig): return shared.JS.make_initializer(proper_sig[i], settings) # extra param, just send a zero + return shared.JS.make_coercion('p%d' % (i-1), proper_sig[i], settings, convert_from=sig[i]) + proper_code = proper_target + '(' + ','.join(map(lambda i: make_emulated_param(i+1), range(len(proper_sig)-1))) + ')' + if proper_sig[0] != 'v': + # proper sig has a return, which the wrapper may or may not use + proper_code = shared.JS.make_coercion(proper_code, proper_sig[0], settings) + if proper_sig[0] != sig[0]: + # first coercion ensured we call the target ok; this one ensures we return the right type in the wrapper + proper_code = shared.JS.make_coercion(proper_code, sig[0], settings, convert_from=proper_sig[0]) + if sig[0] != 'v': + proper_code = 'return ' + proper_code + else: + # proper sig has no return, we may need a fake return + if sig[0] != 'v': + proper_code = 'return ' + shared.JS.make_initializer(sig[0], settings) + name = 'fpemu_%s_%d' % (sig, j) + wrapper = make_func(name, proper_code, params, coercions) + Counter.pre.append(wrapper) + return name if not newline else (name + '\n') + if settings['ASSERTIONS'] <= 1: + return bad if not newline else (bad + '\n') + else: + specific_bad, specific_bad_func = make_bad(j) + Counter.pre.append(specific_bad_func) + return specific_bad if not newline else (specific_bad + '\n') + clean_item = item.replace("asm['", '').replace("']", '') + # when emulating function pointers, we don't need wrappers + # but if relocating, then we also have the copies in-module, and do + # in wasm we never need wrappers though + if clean_item not in implemented_functions and not (settings['EMULATED_FUNCTION_POINTERS'] and not settings['RELOCATABLE']) and not settings['BINARYEN']: + # this is imported into asm, we must wrap it + call_ident = clean_item + if call_ident in metadata['redirects']: call_ident = metadata['redirects'][call_ident] + if not call_ident.startswith('_') and not call_ident.startswith('Math_'): call_ident = '_' + call_ident + code = call_ident + '(' + coerced_params + ')' + if sig[0] != 'v': + # ffis cannot return float + if sig[0] == 'f': code = '+' + code + code = 'return ' + shared.JS.make_coercion(code, sig[0], settings) + code += ';' + Counter.pre.append(make_func(clean_item + '__wrapper', code, params, coercions)) + assert not sig == 'X', 'must know the signature in order to create a wrapper for "%s" (TODO for shared wasm modules)' % item + return clean_item + '__wrapper' + return item if not newline else (item + '\n') + if settings['ASSERTIONS'] >= 2: + debug_tables[sig] = body + body = ','.join(map(fix_item, body)) + return ('\n'.join(Counter.pre), ''.join([raw[:start+1], body, raw[end:]])) + + infos = [make_table(sig, raw) for sig, raw in forwarded_json['Functions']['tables'].iteritems()] + Counter.pre = [] + + function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n' + function_tables_defs += '\n// EMSCRIPTEN_END_FUNCS\n' + function_tables_defs += '\n'.join([info[1] for info in infos]) + return function_tables_defs + + +def make_func(name, code, params, coercions): + return 'function %s(%s) {\n %s %s\n}' % (name, params, coercions, code) + + def global_simd_funcs(access_quote, metadata, settings): # Always import SIMD when building with -s SIMD=1, since in that mode memcpy is SIMD optimized. if not (metadata['simd'] or settings['SIMD']): From 5bf8939c99b3b3e338bf355b17cc92e8c33dd29e Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 15:22:31 -0700 Subject: [PATCH 13/52] Explicitly return tables from make_function_tables_defs --- emscripten.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/emscripten.py b/emscripten.py index 47c7fca166180..69a76b0017da7 100755 --- a/emscripten.py +++ b/emscripten.py @@ -384,10 +384,7 @@ def move_preasm(m): else: pre_tables = '' - in_table = set() - debug_tables = {} - - function_tables_defs = make_function_tables_defs(in_table, debug_tables, implemented_functions, all_implemented, forwarded_json, settings, metadata) + in_table, debug_tables, function_tables_defs = make_function_tables_defs(implemented_functions, all_implemented, forwarded_json, settings, metadata) asm_setup = '' @@ -811,7 +808,10 @@ class Counter: j = 0 -def make_function_tables_defs(in_table, debug_tables, implemented_functions, all_implemented, forwarded_json, settings, metadata): +def make_function_tables_defs(implemented_functions, all_implemented, forwarded_json, settings, metadata): + in_table = set() + debug_tables = {} + def make_params(sig): return ','.join(['p%d' % p for p in range(len(sig)-1)]) def make_coerced_params(sig): return ','.join([shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) def make_coercions(sig): return ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';' @@ -936,7 +936,7 @@ def make_emulated_param(i): function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n' function_tables_defs += '\n// EMSCRIPTEN_END_FUNCS\n' function_tables_defs += '\n'.join([info[1] for info in infos]) - return function_tables_defs + return in_table, debug_tables, function_tables_defs def make_func(name, code, params, coercions): From 38ebfdcbafd17d48c6c00a502c997b794a685f61 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 15:25:26 -0700 Subject: [PATCH 14/52] Update simd text variable name --- emscripten.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/emscripten.py b/emscripten.py index 69a76b0017da7..ea433a54915e8 100755 --- a/emscripten.py +++ b/emscripten.py @@ -961,17 +961,17 @@ def string_contains_any(s, str_list): simd = make_simd_types(metadata, settings) - func_js = '' - func_js += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simd['types']]) + simd_func_text = '' + simd_func_text += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simd['types']]) def generate_symbols(types, funcs): symbols = [' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in types for g in funcs] symbols = filter(lambda x: not string_contains_any(x, nonexisting_simd_symbols), symbols) return ''.join(symbols) - func_js += generate_symbols(simd['int_types'], simd['int_funcs']) - func_js += generate_symbols(simd['float_types'], simd['float_funcs']) - func_js += generate_symbols(simd['bool_types'], simd['bool_funcs']) + simd_func_text += generate_symbols(simd['int_types'], simd['int_funcs']) + simd_func_text += generate_symbols(simd['float_types'], simd['float_funcs']) + simd_func_text += generate_symbols(simd['bool_types'], simd['bool_funcs']) # SIMD conversions (not bitcasts) between same lane sizes: def add_simd_cast(dst, src): @@ -981,19 +981,19 @@ def add_simd_casts(t1, t2): # Bug: Skip importing conversions for int<->uint for now, they don't validate as asm.js. https://bugzilla.mozilla.org/show_bug.cgi?id=1313512 # This is not an issue when building SSEx code, because it doesn't use these. (but it will be an issue if using SIMD.js intrinsics from vector.h to explicitly call these) -# if metadata['simdInt8x16'] and metadata['simdUint8x16']: func_js += add_simd_casts('Int8x16', 'Uint8x16') -# if metadata['simdInt16x8'] and metadata['simdUint16x8']: func_js += add_simd_casts('Int16x8', 'Uint16x8') -# if metadata['simdInt32x4'] and metadata['simdUint32x4']: func_js += add_simd_casts('Int32x4', 'Uint32x4') +# if metadata['simdInt8x16'] and metadata['simdUint8x16']: simd_func_text += add_simd_casts('Int8x16', 'Uint8x16') +# if metadata['simdInt16x8'] and metadata['simdUint16x8']: simd_func_text += add_simd_casts('Int16x8', 'Uint16x8') +# if metadata['simdInt32x4'] and metadata['simdUint32x4']: simd_func_text += add_simd_casts('Int32x4', 'Uint32x4') - if metadata['simdInt32x4'] and metadata['simdFloat32x4']: func_js += add_simd_casts('Int32x4', 'Float32x4') - if metadata['simdUint32x4'] and metadata['simdFloat32x4']: func_js += add_simd_casts('Uint32x4', 'Float32x4') - if metadata['simdInt32x4'] and metadata['simdFloat64x2']: func_js += add_simd_cast('Int32x4', 'Float64x2') # Unofficial, needed for emscripten_int32x4_fromFloat64x2 - if metadata['simdUint32x4'] and metadata['simdFloat64x2']: func_js += add_simd_cast('Uint32x4', 'Float64x2') # Unofficial, needed for emscripten_uint32x4_fromFloat64x2 + if metadata['simdInt32x4'] and metadata['simdFloat32x4']: simd_func_text += add_simd_casts('Int32x4', 'Float32x4') + if metadata['simdUint32x4'] and metadata['simdFloat32x4']: simd_func_text += add_simd_casts('Uint32x4', 'Float32x4') + if metadata['simdInt32x4'] and metadata['simdFloat64x2']: simd_func_text += add_simd_cast('Int32x4', 'Float64x2') # Unofficial, needed for emscripten_int32x4_fromFloat64x2 + if metadata['simdUint32x4'] and metadata['simdFloat64x2']: simd_func_text += add_simd_cast('Uint32x4', 'Float64x2') # Unofficial, needed for emscripten_uint32x4_fromFloat64x2 # Unofficial, Bool64x2 does not yet exist, but needed for Float64x2 comparisons. if metadata['simdFloat64x2']: - func_js += ' var SIMD_Int32x4_fromBool64x2Bits = global.SIMD.Int32x4.fromBool64x2Bits;\n' - return func_js + simd_func_text += ' var SIMD_Int32x4_fromBool64x2Bits = global.SIMD.Int32x4.fromBool64x2Bits;\n' + return simd_func_text def make_simd_types(metadata, settings): From e33e1b3beadc267f4ce47a3b0693375d00ce2a57 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 15:40:32 -0700 Subject: [PATCH 15/52] Extract create_receiving --- emscripten.py | 74 ++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/emscripten.py b/emscripten.py index ea433a54915e8..e0ee3d45c4cfb 100755 --- a/emscripten.py +++ b/emscripten.py @@ -653,44 +653,11 @@ def math_fix(g): # sent data the_global = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' - # received - receiving = '' - if settings['ASSERTIONS']: - # assert on the runtime being in a valid state when calling into compiled code. The only exceptions are - # some support code - receiving = '\n'.join(['var real_' + s + ' = asm["' + s + '"]; asm["' + s + '''"] = function() { -assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)'); -assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)'); -return real_''' + s + '''.apply(null, arguments); -}; -''' for s in exported_implemented_functions if s not in ['_memcpy', '_memset', 'runPostSets', '_emscripten_replace_memory', '__start_module']]) - - if not settings['SWAPPABLE_ASM_MODULE']: - receiving += ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables]) - else: - receiving += 'Module["asm"] = asm;\n' + ';\n'.join(['var ' + s + ' = Module["' + s + '"] = function() { return Module["asm"]["' + s + '"].apply(null, arguments) }' for s in exported_implemented_functions + function_tables]) - receiving += ';\n' - if settings['EXPORT_FUNCTION_TABLES'] and not settings['BINARYEN']: - for table in forwarded_json['Functions']['tables'].values(): - tableName = table.split()[1] - table = table.replace('var ' + tableName, 'var ' + tableName + ' = Module["' + tableName + '"]') - receiving += table + '\n' + receiving, asm_setup, final_function_tables = create_receiving(asm_setup, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, forwarded_json, settings) if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs) + (function_tables_defs.count('\n') * len(' ')), len(exports), len(the_global), len(sending), len(receiving)])) - final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs - if settings.get('EMULATED_FUNCTION_POINTERS'): - asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' - receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in forwarded_json['Functions']['tables']]) - if not settings['BINARYEN']: - for sig in forwarded_json['Functions']['tables'].keys(): - name = 'FUNCTION_TABLE_' + sig - fullname = name if not settings['SIDE_MODULE'] else ('SIDE_' + name) - receiving += 'Module["' + name + '"] = ' + fullname + ';\n' - - final_function_tables = final_function_tables.replace("asm['", '').replace("']", '').replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_').replace('var dynCall_', '//') - if DEBUG: logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) @@ -1059,6 +1026,45 @@ def make_simd_types(metadata, settings): 'intbool_funcs': simd_intbool_funcs, } + +def create_receiving(asm_setup, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, forwarded_json, settings): + receiving = '' + if settings['ASSERTIONS']: + # assert on the runtime being in a valid state when calling into compiled code. The only exceptions are + # some support code + receiving = '\n'.join(['var real_' + s + ' = asm["' + s + '"]; asm["' + s + '''"] = function() { +assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)'); +assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)'); +return real_''' + s + '''.apply(null, arguments); +}; +''' for s in exported_implemented_functions if s not in ['_memcpy', '_memset', 'runPostSets', '_emscripten_replace_memory', '__start_module']]) + + if not settings['SWAPPABLE_ASM_MODULE']: + receiving += ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables]) + else: + receiving += 'Module["asm"] = asm;\n' + ';\n'.join(['var ' + s + ' = Module["' + s + '"] = function() { return Module["asm"]["' + s + '"].apply(null, arguments) }' for s in exported_implemented_functions + function_tables]) + receiving += ';\n' + + if settings['EXPORT_FUNCTION_TABLES'] and not settings['BINARYEN']: + for table in forwarded_json['Functions']['tables'].values(): + tableName = table.split()[1] + table = table.replace('var ' + tableName, 'var ' + tableName + ' = Module["' + tableName + '"]') + receiving += table + '\n' + + final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs + if settings.get('EMULATED_FUNCTION_POINTERS'): + asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' + receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in forwarded_json['Functions']['tables']]) + if not settings['BINARYEN']: + for sig in forwarded_json['Functions']['tables'].keys(): + name = 'FUNCTION_TABLE_' + sig + fullname = name if not settings['SIDE_MODULE'] else ('SIDE_' + name) + receiving += 'Module["' + name + '"] = ' + fullname + ';\n' + + final_function_tables = final_function_tables.replace("asm['", '').replace("']", '').replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_').replace('var dynCall_', '//') + return receiving, asm_setup, final_function_tables + + def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG): if DEBUG: From 86e2140891b460125926eee8cd7f22bfcc893e80 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 15:49:20 -0700 Subject: [PATCH 16/52] Extract create_asm_globals --- emscripten.py | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/emscripten.py b/emscripten.py index e0ee3d45c4cfb..3176c40a5aadc 100755 --- a/emscripten.py +++ b/emscripten.py @@ -392,8 +392,6 @@ def move_preasm(m): for sig in forwarded_json['Functions']['tables']: asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' - maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] - fundamentals = ['Math'] fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] fundamentals += ['NaN', 'Infinity'] @@ -402,10 +400,6 @@ def move_preasm(m): if settings['ALLOW_MEMORY_GROWTH']: fundamentals.append('byteLength') math_envs = [] - provide_fround = settings['PRECISE_F32'] or settings['SIMD'] - - if provide_fround: maths += ['Math.fround'] - def get_function_pointer_error(sig): if settings['ASSERTIONS'] <= 1: extra = ' Module["printErr"]("Build with ASSERTIONS=2 for more info.");' @@ -636,19 +630,12 @@ def check(extern): return '' for extern in metadata['externs']: asm_setup += 'var g$' + extern + ' = function() { ' + check(extern) + ' return ' + side + 'Module["' + extern + '"] };\n' - def math_fix(g): - return g if not g.startswith('Math_') else g.split('_')[1] - asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global' + access_quote(g) + ';\n' for g in maths]) - asm_global_funcs += ''.join([' var ' + g + '=env' + access_quote(math_fix(g)) + ';\n' for g in basic_funcs + global_funcs]) - asm_global_funcs += global_simd_funcs(access_quote, metadata, settings) - - if settings['USE_PTHREADS']: - asm_global_funcs += ''.join([' var Atomics_' + ty + '=global' + access_quote('Atomics') + access_quote(ty) + ';\n' for ty in ['load', 'store', 'exchange', 'compareExchange', 'add', 'sub', 'and', 'or', 'xor']]) - asm_global_vars = ''.join([' var ' + g + '=env' + access_quote(g) + '|0;\n' for g in basic_vars + global_vars]) + provide_fround = settings['PRECISE_F32'] or settings['SIMD'] - if settings['BINARYEN'] and settings['SIDE_MODULE']: - asm_global_vars += '\n var STACKTOP = 0, STACK_MAX = 0;\n' # wasm side modules internally define their stack, these are set at module startup time + bg_funcs = basic_funcs + global_funcs + bg_vars = basic_vars + global_vars + asm_global_funcs, asm_global_vars = create_asm_globals(provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings) # sent data the_global = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' @@ -910,6 +897,28 @@ def make_func(name, code, params, coercions): return 'function %s(%s) {\n %s %s\n}' % (name, params, coercions, code) +def math_fix(g): + return g if not g.startswith('Math_') else g.split('_')[1] + + +def create_asm_globals(provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings): + maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] + if provide_fround: + maths += ['Math.fround'] + + asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global' + access_quote(g) + ';\n' for g in maths]) + asm_global_funcs += ''.join([' var ' + g + '=env' + access_quote(math_fix(g)) + ';\n' for g in bg_funcs]) + asm_global_funcs += global_simd_funcs(access_quote, metadata, settings) + if settings['USE_PTHREADS']: + asm_global_funcs += ''.join([' var Atomics_' + ty + '=global' + access_quote('Atomics') + access_quote(ty) + ';\n' for ty in ['load', 'store', 'exchange', 'compareExchange', 'add', 'sub', 'and', 'or', 'xor']]) + + asm_global_vars = ''.join([' var ' + g + '=env' + access_quote(g) + '|0;\n' for g in bg_vars]) + if settings['BINARYEN'] and settings['SIDE_MODULE']: + asm_global_vars += '\n var STACKTOP = 0, STACK_MAX = 0;\n' # wasm side modules internally define their stack, these are set at module startup time + + return asm_global_funcs, asm_global_vars + + def global_simd_funcs(access_quote, metadata, settings): # Always import SIMD when building with -s SIMD=1, since in that mode memcpy is SIMD optimized. if not (metadata['simd'] or settings['SIMD']): From 367ad78a2153ee0a93176da137a0d369f44b8efe Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 16:21:49 -0700 Subject: [PATCH 17/52] Rename Counter variables to ble clearer that they are separate things --- emscripten.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/emscripten.py b/emscripten.py index 3176c40a5aadc..adab6cb0baa56 100755 --- a/emscripten.py +++ b/emscripten.py @@ -757,12 +757,12 @@ def unfloat(s): return 'd' if s == 'f' else s -class Counter: - i = 0 - j = 0 - - def make_function_tables_defs(implemented_functions, all_implemented, forwarded_json, settings, metadata): + class Counter: + next_bad_item = 0 + next_item = 0 + pre = [] + in_table = set() debug_tables = {} @@ -789,8 +789,8 @@ def make_table(sig, raw): coerced_params = make_coerced_params(sig) coercions = make_coercions(sig) def make_bad(target=None): - i = Counter.i - Counter.i += 1 + i = Counter.next_bad_item + Counter.next_bad_item += 1 if target is None: target = i name = 'b' + str(i) if not settings['ASSERTIONS']: @@ -812,22 +812,21 @@ def make_bad(target=None): def receive(item): if item == '0': return item - else: - if item in all_implemented: - in_table.add(item) - return "asm['" + item + "']" - else: - return item # this is not implemented; it would normally be wrapped, but with emulation, we just use it directly outside + if item not in all_implemented: + # this is not implemented; it would normally be wrapped, but with emulation, we just use it directly outside + return item + in_table.add(item) + return "asm['" + item + "']" body = map(receive, body) for j in range(settings['RESERVED_FUNCTION_POINTERS']): curr = 'jsCall_%s_%s' % (sig, j) body[settings['FUNCTION_POINTER_ALIGNMENT'] * (1 + j)] = curr implemented_functions.add(curr) - Counter.j = 0 + Counter.next_item = 0 def fix_item(item): - j = Counter.j - Counter.j += 1 - newline = Counter.j % 30 == 29 + j = Counter.next_item + Counter.next_item += 1 + newline = Counter.next_item % 30 == 29 if item == '0': if j > 0 and settings['EMULATE_FUNCTION_POINTER_CASTS'] and j in function_pointer_targets: # emulate all non-null pointer calls, if asked to proper_sig, proper_target = function_pointer_targets[j] From 5d799d2f7e67330420feb2a97325311bdf60d640 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 16:28:56 -0700 Subject: [PATCH 18/52] Extract create_the_global --- emscripten.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/emscripten.py b/emscripten.py index adab6cb0baa56..7a12b5b5582da 100755 --- a/emscripten.py +++ b/emscripten.py @@ -392,12 +392,6 @@ def move_preasm(m): for sig in forwarded_json['Functions']['tables']: asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' - fundamentals = ['Math'] - fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] - fundamentals += ['NaN', 'Infinity'] - if metadata['simd'] or settings['SIMD']: # Always import SIMD when building with -s SIMD=1, since in that mode memcpy is SIMD optimized. - fundamentals += ['SIMD'] - if settings['ALLOW_MEMORY_GROWTH']: fundamentals.append('byteLength') math_envs = [] def get_function_pointer_error(sig): @@ -637,8 +631,7 @@ def check(extern): bg_vars = basic_vars + global_vars asm_global_funcs, asm_global_vars = create_asm_globals(provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings) - # sent data - the_global = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' + the_global = create_the_global(metadata, settings) sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' receiving, asm_setup, final_function_tables = create_receiving(asm_setup, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, forwarded_json, settings) @@ -1035,6 +1028,18 @@ def make_simd_types(metadata, settings): } +def create_the_global(metadata, settings): + fundamentals = ['Math'] + fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] + fundamentals += ['NaN', 'Infinity'] + if metadata['simd'] or settings['SIMD']: + # Always import SIMD when building with -s SIMD=1, since in that mode memcpy is SIMD optimized. + fundamentals += ['SIMD'] + if settings['ALLOW_MEMORY_GROWTH']: + fundamentals.append('byteLength') + return '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' + + def create_receiving(asm_setup, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, forwarded_json, settings): receiving = '' if settings['ASSERTIONS']: From 574593bf778c9d3b9a06e83599ca20089c6d2106 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Tue, 11 Apr 2017 16:35:20 -0700 Subject: [PATCH 19/52] Extract create_asm_runtime_funcs --- emscripten.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/emscripten.py b/emscripten.py index 7a12b5b5582da..f0741bafa88b5 100755 --- a/emscripten.py +++ b/emscripten.py @@ -461,12 +461,6 @@ def keyfunc(other): if not settings['SIDE_MODULE']: asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n' - asm_runtime_funcs = [] - if not (settings['BINARYEN'] and settings['SIDE_MODULE']): - asm_runtime_funcs += ['stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', 'setThrew'] - if not settings['RELOCATABLE']: - asm_runtime_funcs += ['setTempRet0', 'getTempRet0'] - else: basic_funcs += ['setTempRet0', 'getTempRet0'] asm_setup += 'var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;\n' @@ -487,18 +481,6 @@ def table_size(table): need_asyncify = '_emscripten_alloc_async_context' in exported_implemented_functions if need_asyncify: basic_vars += ['___async', '___async_unwind', '___async_retval', '___async_cur_frame'] - asm_runtime_funcs += ['setAsync'] - - if settings.get('EMTERPRETIFY'): - asm_runtime_funcs += ['emterpret'] - if settings.get('EMTERPRETIFY_ASYNC'): - asm_runtime_funcs += ['setAsyncState', 'emtStackSave', 'emtStackRestore'] - - if settings['SAFE_HEAP']: - asm_runtime_funcs += ['setDynamicTop'] - - if settings['ONLY_MY_CODE']: - asm_runtime_funcs = [] # function tables if not settings['EMULATED_FUNCTION_POINTERS']: @@ -591,6 +573,8 @@ def table_size(table): if not (settings['BINARYEN'] and settings['SIDE_MODULE']): exported_implemented_functions += ['setThrew'] + asm_runtime_funcs = create_asm_runtime_funcs(need_asyncify, settings) + all_exported = exported_implemented_functions + asm_runtime_funcs + function_tables exported_implemented_functions = list(set(exported_implemented_functions)) if settings['EMULATED_FUNCTION_POINTERS']: @@ -1028,6 +1012,25 @@ def make_simd_types(metadata, settings): } +def create_asm_runtime_funcs(need_asyncify, settings): + funcs = [] + if not (settings['BINARYEN'] and settings['SIDE_MODULE']): + funcs += ['stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', 'setThrew'] + if not settings['RELOCATABLE']: + funcs += ['setTempRet0', 'getTempRet0'] + if settings['SAFE_HEAP']: + funcs += ['setDynamicTop'] + if settings['ONLY_MY_CODE']: + funcs = [] + if settings.get('EMTERPRETIFY'): + funcs += ['emterpret'] + if settings.get('EMTERPRETIFY_ASYNC'): + funcs += ['setAsyncState', 'emtStackSave', 'emtStackRestore'] + if need_asyncify: + funcs += ['setAsync'] + return funcs + + def create_the_global(metadata, settings): fundamentals = ['Math'] fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] From a83aef1a6f5af39562566edb0ddd2e98ec9fdee8 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Wed, 12 Apr 2017 14:00:18 -0700 Subject: [PATCH 20/52] Remove unused forwarded_json argument --- emscripten.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emscripten.py b/emscripten.py index f0741bafa88b5..b2054515ff6be 100755 --- a/emscripten.py +++ b/emscripten.py @@ -360,7 +360,7 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, pre = memory_and_global_initializers(pre, metadata, mem_init, settings) pre, funcs_js = get_js_funcs(pre, funcs) exported_implemented_functions, all_implemented = get_exported_implemented_functions(metadata, forwarded_json, settings) - implemented_functions = get_implemented_functions(pre, metadata, forwarded_json, settings, all_implemented) + implemented_functions = get_implemented_functions(pre, metadata, settings, all_implemented) if settings['BINARYEN'] and settings['SIDE_MODULE']: assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)' pre = include_asm_consts(pre, metadata, forwarded_json) @@ -683,7 +683,7 @@ def get_exported_implemented_functions(metadata, forwarded_json, settings): return exported_implemented_functions, all_implemented -def get_implemented_functions(pre, metadata, forwarded_json, settings, all_implemented): +def get_implemented_functions(pre, metadata, settings, all_implemented): implemented_functions = set(metadata['implementedFunctions']) if settings['ASSERTIONS'] and settings.get('ORIGINAL_EXPORTED_FUNCTIONS'): original_exports = settings['ORIGINAL_EXPORTED_FUNCTIONS'] From d5513a2d805b742409bc64ac752b7aaa8b996237 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Wed, 12 Apr 2017 14:21:36 -0700 Subject: [PATCH 21/52] Refactor asm_consts logic --- emscripten.py | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/emscripten.py b/emscripten.py index b2054515ff6be..1ad3f656a4219 100755 --- a/emscripten.py +++ b/emscripten.py @@ -361,9 +361,7 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, pre, funcs_js = get_js_funcs(pre, funcs) exported_implemented_functions, all_implemented = get_exported_implemented_functions(metadata, forwarded_json, settings) implemented_functions = get_implemented_functions(pre, metadata, settings, all_implemented) - if settings['BINARYEN'] and settings['SIDE_MODULE']: - assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)' - pre = include_asm_consts(pre, metadata, forwarded_json) + pre = include_asm_consts(pre, forwarded_json, metadata, settings) #if DEBUG: outfile.write('// pre\n') outfile.write(pre) pre = None @@ -699,7 +697,29 @@ def get_implemented_functions(pre, metadata, settings, all_implemented): return implemented_functions -def include_asm_consts(pre, metadata, forwarded_json): +def include_asm_consts(pre, forwarded_json, metadata, settings): + if settings['BINARYEN'] and settings['SIDE_MODULE']: + assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)' + + asm_consts, all_sigs = all_asm_consts(metadata) + asm_const_funcs = [] + for sig in set(all_sigs): + forwarded_json['Functions']['libraryFunctions']['_emscripten_asm_const_' + sig] = 1 + args = ['a%d' % i for i in range(len(sig)-1)] + all_args = ['code'] + args + asm_const_funcs.append(r''' +function _emscripten_asm_const_%s(%s) { + return ASM_CONSTS[code](%s); +}''' % (sig.encode('utf-8'), ', '.join(all_args), ', '.join(args))) + + asm_consts_text = '\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + asm_funcs_text = '\n'.join(asm_const_funcs) + '\n' + + body_marker = '// === Body ===' + return pre.replace(body_marker, body_marker + '\n' + asm_consts_text + asm_funcs_text) + + +def all_asm_consts(metadata): asm_consts = [0]*len(metadata['asmConsts']) all_sigs = [] for k, v in metadata['asmConsts'].iteritems(): @@ -715,18 +735,7 @@ def include_asm_consts(pre, metadata, forwarded_json): const = 'function(' + ', '.join(args) + ') ' + const asm_consts[int(k)] = const all_sigs += sigs - - asm_const_funcs = [] - for sig in set(all_sigs): - forwarded_json['Functions']['libraryFunctions']['_emscripten_asm_const_' + sig] = 1 - args = ['a%d' % i for i in range(len(sig)-1)] - all_args = ['code'] + args - asm_const_funcs.append(r''' -function _emscripten_asm_const_%s(%s) { - return ASM_CONSTS[code](%s); -}''' % (sig.encode('utf-8'), ', '.join(all_args), ', '.join(args))) - - return pre.replace('// === Body ===', '// === Body ===\n\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n' + '\n'.join(asm_const_funcs) + '\n') + return asm_consts, all_sigs def unfloat(s): From 0de0b50edc073762e0a037d24970706825b0d587 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Wed, 12 Apr 2017 14:50:17 -0700 Subject: [PATCH 22/52] Replace repeated use of forwarded_json['Functions']['tables'] with function_table_data variable --- emscripten.py | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/emscripten.py b/emscripten.py index 1ad3f656a4219..ea56cbb851e7f 100755 --- a/emscripten.py +++ b/emscripten.py @@ -96,9 +96,9 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) with ToolchainProfiler.profile_block('function_tables_and_exports'): - post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) + post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) with ToolchainProfiler.profile_block('finalize_output'): - finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG) + finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG) success = True @@ -351,7 +351,7 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, # merge in information from llvm backend - forwarded_json['Functions']['tables'] = metadata['tables'] + function_table_data = metadata['tables'] pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') @@ -359,7 +359,7 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, pre = memory_and_global_initializers(pre, metadata, mem_init, settings) pre, funcs_js = get_js_funcs(pre, funcs) - exported_implemented_functions, all_implemented = get_exported_implemented_functions(metadata, forwarded_json, settings) + exported_implemented_functions, all_implemented = get_exported_implemented_functions(metadata, function_table_data, forwarded_json, settings) implemented_functions = get_implemented_functions(pre, metadata, settings, all_implemented) pre = include_asm_consts(pre, forwarded_json, metadata, settings) #if DEBUG: outfile.write('// pre\n') @@ -376,18 +376,18 @@ def move_preasm(m): if not settings['BOOTSTRAPPING_STRUCT_INFO'] and len(funcs_js) > 1: funcs_js[1] = re.sub(r'/\* PRE_ASM \*/(.*)\n', move_preasm, funcs_js[1]) - if 'pre' in forwarded_json['Functions']['tables']: - pre_tables = forwarded_json['Functions']['tables']['pre'] - del forwarded_json['Functions']['tables']['pre'] + if 'pre' in function_table_data: + pre_tables = function_table_data['pre'] + del function_table_data['pre'] else: pre_tables = '' - in_table, debug_tables, function_tables_defs = make_function_tables_defs(implemented_functions, all_implemented, forwarded_json, settings, metadata) + in_table, debug_tables, function_tables_defs = make_function_tables_defs(implemented_functions, all_implemented, function_table_data, settings, metadata) asm_setup = '' if settings['ASSERTIONS'] >= 2: - for sig in forwarded_json['Functions']['tables']: + for sig in function_table_data: asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' math_envs = [] @@ -400,7 +400,7 @@ def get_function_pointer_error(sig): pointer = ' \'" + x + "\' ' extra = ' Module["printErr"]("This pointer might make sense in another type signature: ' # sort signatures, attempting to show most likely related ones first - sigs = forwarded_json['Functions']['tables'].keys() + sigs = function_table_data.keys() def keyfunc(other): ret = 0 minlen = min(len(other), len(sig)) @@ -436,7 +436,7 @@ def keyfunc(other): basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_LOAD_D', 'SAFE_HEAP_STORE', 'SAFE_HEAP_STORE_D', 'SAFE_FT_MASK'] if settings['ASSERTIONS']: if settings['ASSERTIONS'] >= 2: import difflib - for sig in forwarded_json['Functions']['tables'].iterkeys(): + for sig in function_table_data.iterkeys(): basic_funcs += ['nullFunc_' + sig] asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig) + 'abort(x) }\n' @@ -469,7 +469,7 @@ def table_size(table): return 0 return table_contents.count(',') + 1 - table_total_size = sum(map(table_size, forwarded_json['Functions']['tables'].values())) + table_total_size = sum(map(table_size, function_table_data.values())) asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size if not settings['EMULATED_FUNCTION_POINTERS']: asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size @@ -482,12 +482,12 @@ def table_size(table): # function tables if not settings['EMULATED_FUNCTION_POINTERS']: - function_tables = ['dynCall_' + table for table in forwarded_json['Functions']['tables']] + function_tables = ['dynCall_' + table for table in function_table_data] else: function_tables = [] function_tables_impls = [] - for sig in forwarded_json['Functions']['tables'].iterkeys(): + for sig in function_table_data.iterkeys(): args = ','.join(['a' + str(i) for i in range(1, len(sig))]) arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) @@ -616,14 +616,14 @@ def check(extern): the_global = create_the_global(metadata, settings) sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' - receiving, asm_setup, final_function_tables = create_receiving(asm_setup, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, forwarded_json, settings) + receiving, asm_setup, final_function_tables = create_receiving(asm_setup, function_table_data, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, settings) if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs) + (function_tables_defs.count('\n') * len(' ')), len(exports), len(the_global), len(sending), len(receiving)])) if DEBUG: logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) - return post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json + return post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json def memory_and_global_initializers(pre, metadata, mem_init, settings): @@ -659,7 +659,7 @@ def get_js_funcs(pre, funcs): return pre, funcs_js -def get_exported_implemented_functions(metadata, forwarded_json, settings): +def get_exported_implemented_functions(metadata, function_table_data, forwarded_json, settings): # merge forwarded data settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] all_exported_functions = set(shared.expand_response(settings['EXPORTED_FUNCTIONS'])) # both asm.js and otherwise @@ -667,7 +667,7 @@ def get_exported_implemented_functions(metadata, forwarded_json, settings): for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented all_exported_functions.add('_' + additional_export) if settings['EXPORT_FUNCTION_TABLES']: - for table in forwarded_json['Functions']['tables'].values(): + for table in function_table_data.values(): for func in table.split('[')[1].split(']')[0].split(','): if func[0] == '_': all_exported_functions.add(func) @@ -743,7 +743,7 @@ def unfloat(s): return 'd' if s == 'f' else s -def make_function_tables_defs(implemented_functions, all_implemented, forwarded_json, settings, metadata): +def make_function_tables_defs(implemented_functions, all_implemented, function_table_data, settings, metadata): class Counter: next_bad_item = 0 next_item = 0 @@ -759,7 +759,7 @@ def make_coercions(sig): return ';'.join(['p%d = %s' % (p, shared.JS.make_coerci # when emulating function pointer casts, we need to know what is the target of each pointer if settings['EMULATE_FUNCTION_POINTER_CASTS']: function_pointer_targets = {} - for sig, table in forwarded_json['Functions']['tables'].iteritems(): + for sig, table in function_table_data.iteritems(): start = table.index('[') end = table.rindex(']') body = table[start+1:end].split(',') @@ -869,7 +869,7 @@ def make_emulated_param(i): body = ','.join(map(fix_item, body)) return ('\n'.join(Counter.pre), ''.join([raw[:start+1], body, raw[end:]])) - infos = [make_table(sig, raw) for sig, raw in forwarded_json['Functions']['tables'].iteritems()] + infos = [make_table(sig, raw) for sig, raw in function_table_data.iteritems()] Counter.pre = [] function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n' @@ -1052,7 +1052,7 @@ def create_the_global(metadata, settings): return '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' -def create_receiving(asm_setup, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, forwarded_json, settings): +def create_receiving(asm_setup, function_table_data, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, settings): receiving = '' if settings['ASSERTIONS']: # assert on the runtime being in a valid state when calling into compiled code. The only exceptions are @@ -1071,7 +1071,7 @@ def create_receiving(asm_setup, function_tables, function_tables_defs, function_ receiving += ';\n' if settings['EXPORT_FUNCTION_TABLES'] and not settings['BINARYEN']: - for table in forwarded_json['Functions']['tables'].values(): + for table in function_table_data.values(): tableName = table.split()[1] table = table.replace('var ' + tableName, 'var ' + tableName + ' = Module["' + tableName + '"]') receiving += table + '\n' @@ -1079,9 +1079,9 @@ def create_receiving(asm_setup, function_tables, function_tables_defs, function_ final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs if settings.get('EMULATED_FUNCTION_POINTERS'): asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' - receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in forwarded_json['Functions']['tables']]) + receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in function_table_data]) if not settings['BINARYEN']: - for sig in forwarded_json['Functions']['tables'].keys(): + for sig in function_table_data.keys(): name = 'FUNCTION_TABLE_' + sig fullname = name if not settings['SIDE_MODULE'] else ('SIDE_' + name) receiving += 'Module["' + name + '"] = ' + fullname + ';\n' @@ -1090,7 +1090,7 @@ def create_receiving(asm_setup, function_tables, function_tables_defs, function_ return receiving, asm_setup, final_function_tables -def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, forwarded_json, settings, outfile, DEBUG): +def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG): if DEBUG: logging.debug('emscript: python processing: finalize') @@ -1464,7 +1464,7 @@ def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm # Set function table masks masks = {} max_mask = 0 - for sig, table in forwarded_json['Functions']['tables'].iteritems(): + for sig, table in function_table_data.iteritems(): mask = table.count(',') masks[sig] = str(mask) max_mask = max(mask, max_mask) @@ -1479,7 +1479,7 @@ def fix(m): if settings['SIDE_MODULE']: funcs_js.append(''' Runtime.registerFunctions(%(sigs)s, Module); -''' % { 'sigs': str(map(str, forwarded_json['Functions']['tables'].keys())) }) +''' % { 'sigs': str(map(str, function_table_data.keys())) }) for i in range(len(funcs_js)): # do this loop carefully to save memory if WINDOWS: funcs_js[i] = funcs_js[i].replace('\r\n', '\n') # Normalize to UNIX line endings, otherwise writing to text file will duplicate \r\n to \r\r\n! From 9c61ecc01acbe1befa0807498bab8a7cf22aee6b Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Wed, 12 Apr 2017 16:28:31 -0700 Subject: [PATCH 23/52] Make get_function_pointer_error top-level, unconditionally import difflib --- emscripten.py | 72 ++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/emscripten.py b/emscripten.py index ea56cbb851e7f..5bec231e5a3e1 100755 --- a/emscripten.py +++ b/emscripten.py @@ -13,6 +13,7 @@ if __name__ == '__main__': ToolchainProfiler.record_process_start() +import difflib import os, sys, json, optparse, subprocess, re, time, logging from tools import shared @@ -392,36 +393,6 @@ def move_preasm(m): math_envs = [] - def get_function_pointer_error(sig): - if settings['ASSERTIONS'] <= 1: - extra = ' Module["printErr"]("Build with ASSERTIONS=2 for more info.");' - pointer = ' ' - else: - pointer = ' \'" + x + "\' ' - extra = ' Module["printErr"]("This pointer might make sense in another type signature: ' - # sort signatures, attempting to show most likely related ones first - sigs = function_table_data.keys() - def keyfunc(other): - ret = 0 - minlen = min(len(other), len(sig)) - maxlen = min(len(other), len(sig)) - if other.startswith(sig) or sig.startswith(other): ret -= 1000 # prioritize prefixes, could be dropped params - ret -= 133*difflib.SequenceMatcher(a=other, b=sig).ratio() # prioritize on diff similarity - ret += 15*abs(len(other) - len(sig))/float(maxlen) # deprioritize the bigger the length difference is - for i in range(minlen): - if other[i] == sig[i]: ret -= 5/float(maxlen) # prioritize on identically-placed params - ret += 20*len(other) # deprioritize on length - return ret - sigs.sort(key=keyfunc) - for other in sigs: - if other != sig: - extra += other + ': " + debug_table_' + other + '[x] + " ' - extra += '"); ' - return 'Module["printErr"]("Invalid function pointer' + pointer + 'called with signature \'' + sig + '\'. ' + \ - 'Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? ' + \ - 'Or calling a function with an incorrect type, which will fail? ' + \ - '(it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)' + \ - '"); ' + extra basic_funcs = ['abort', 'assert', 'enlargeMemory', 'getTotalMemory'] + [m.replace('.', '_') for m in math_envs] if settings['ABORTING_MALLOC']: basic_funcs += ['abortOnCannotGrowMemory'] @@ -435,10 +406,9 @@ def keyfunc(other): else: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_LOAD_D', 'SAFE_HEAP_STORE', 'SAFE_HEAP_STORE_D', 'SAFE_FT_MASK'] if settings['ASSERTIONS']: - if settings['ASSERTIONS'] >= 2: import difflib for sig in function_table_data.iterkeys(): basic_funcs += ['nullFunc_' + sig] - asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig) + 'abort(x) }\n' + asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_data, settings) + 'abort(x) }\n' basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] if not (settings['BINARYEN'] and settings['SIDE_MODULE']): @@ -534,7 +504,7 @@ def table_size(table): else: table_read = table_access + '[x]' prelude = ''' - if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig)) + if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_data, settings)) asm_setup += ''' function ftCall_%s(%s) {%s return %s(%s); @@ -886,6 +856,42 @@ def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1] +def get_function_pointer_error(sig, function_table_data, settings): + if settings['ASSERTIONS'] <= 1: + extra = ' Module["printErr"]("Build with ASSERTIONS=2 for more info.");' + pointer = ' ' + else: + pointer = ' \'" + x + "\' ' + extra = ' Module["printErr"]("This pointer might make sense in another type signature: ' + # sort signatures, attempting to show most likely related ones first + sigs = function_table_data.keys() + sigs.sort(key=signature_sort_key(sig)) + for other in sigs: + if other != sig: + extra += other + ': " + debug_table_' + other + '[x] + " ' + extra += '"); ' + return 'Module["printErr"]("Invalid function pointer' + pointer + 'called with signature \'' + sig + '\'. ' + \ + 'Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? ' + \ + 'Or calling a function with an incorrect type, which will fail? ' + \ + '(it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)' + \ + '"); ' + extra + + +def signature_sort_key(sig): + def closure(other): + ret = 0 + minlen = min(len(other), len(sig)) + maxlen = min(len(other), len(sig)) + if other.startswith(sig) or sig.startswith(other): ret -= 1000 # prioritize prefixes, could be dropped params + ret -= 133*difflib.SequenceMatcher(a=other, b=sig).ratio() # prioritize on diff similarity + ret += 15*abs(len(other) - len(sig))/float(maxlen) # deprioritize the bigger the length difference is + for i in range(minlen): + if other[i] == sig[i]: ret -= 5/float(maxlen) # prioritize on identically-placed params + ret += 20*len(other) # deprioritize on length + return ret + return closure + + def create_asm_globals(provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings): maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] if provide_fround: From 276c7e2aaa3a64dc2937f9c0c7999118581d631b Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Wed, 12 Apr 2017 16:53:00 -0700 Subject: [PATCH 24/52] Reduce input/output of create_receiving --- emscripten.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/emscripten.py b/emscripten.py index 5bec231e5a3e1..cc36b28df02c4 100755 --- a/emscripten.py +++ b/emscripten.py @@ -586,7 +586,12 @@ def check(extern): the_global = create_the_global(metadata, settings) sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' - receiving, asm_setup, final_function_tables = create_receiving(asm_setup, function_table_data, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, settings) + receiving = create_receiving(function_table_data, function_tables, function_tables_defs, exported_implemented_functions, settings) + + final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs + if settings.get('EMULATED_FUNCTION_POINTERS'): + asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' + final_function_tables = final_function_tables.replace("asm['", '').replace("']", '').replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_').replace('var dynCall_', '//') if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs) + (function_tables_defs.count('\n') * len(' ')), len(exports), len(the_global), len(sending), len(receiving)])) @@ -1058,7 +1063,7 @@ def create_the_global(metadata, settings): return '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' -def create_receiving(asm_setup, function_table_data, function_tables, function_tables_defs, function_tables_impls, exported_implemented_functions, settings): +def create_receiving(function_table_data, function_tables, function_tables_defs, exported_implemented_functions, settings): receiving = '' if settings['ASSERTIONS']: # assert on the runtime being in a valid state when calling into compiled code. The only exceptions are @@ -1082,9 +1087,7 @@ def create_receiving(asm_setup, function_table_data, function_tables, function_t table = table.replace('var ' + tableName, 'var ' + tableName + ' = Module["' + tableName + '"]') receiving += table + '\n' - final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs if settings.get('EMULATED_FUNCTION_POINTERS'): - asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in function_table_data]) if not settings['BINARYEN']: for sig in function_table_data.keys(): @@ -1092,8 +1095,7 @@ def create_receiving(asm_setup, function_table_data, function_tables, function_t fullname = name if not settings['SIDE_MODULE'] else ('SIDE_' + name) receiving += 'Module["' + name + '"] = ' + fullname + ';\n' - final_function_tables = final_function_tables.replace("asm['", '').replace("']", '').replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_').replace('var dynCall_', '//') - return receiving, asm_setup, final_function_tables + return receiving def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG): From ec63347695ffb1fa85c4a3401b4c108105cc4314 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 11:30:27 -0700 Subject: [PATCH 25/52] Add line breaks --- emscripten.py | 52 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/emscripten.py b/emscripten.py index cc36b28df02c4..30e7bdde9d0a2 100755 --- a/emscripten.py +++ b/emscripten.py @@ -97,9 +97,14 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) with ToolchainProfiler.profile_block('function_tables_and_exports'): - post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) + (post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, + asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, + forwarded_json) = function_tables_and_exports(funcs, metadata, mem_init, glue, + forwarded_data, settings, outfile, DEBUG) with ToolchainProfiler.profile_block('finalize_output'): - finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG) + finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, + receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, + final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG) success = True @@ -360,7 +365,8 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, pre = memory_and_global_initializers(pre, metadata, mem_init, settings) pre, funcs_js = get_js_funcs(pre, funcs) - exported_implemented_functions, all_implemented = get_exported_implemented_functions(metadata, function_table_data, forwarded_json, settings) + exported_implemented_functions, all_implemented = get_exported_implemented_functions( + metadata, function_table_data, forwarded_json, settings) implemented_functions = get_implemented_functions(pre, metadata, settings, all_implemented) pre = include_asm_consts(pre, forwarded_json, metadata, settings) #if DEBUG: outfile.write('// pre\n') @@ -383,7 +389,8 @@ def move_preasm(m): else: pre_tables = '' - in_table, debug_tables, function_tables_defs = make_function_tables_defs(implemented_functions, all_implemented, function_table_data, settings, metadata) + in_table, debug_tables, function_tables_defs = make_function_tables_defs( + implemented_functions, all_implemented, function_table_data, settings, metadata) asm_setup = '' @@ -523,7 +530,10 @@ def table_size(table): if settings['EMULATED_FUNCTION_POINTERS'] == 1: body = final_return else: - body = 'if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + mini_coerced_params + ')', sig[0], settings, ffi_arg=True) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + final_return + body = ('if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + + mini_coerced_params + ')', sig[0], settings, ffi_arg=True) + '; ' + + ('return;' if sig[0] == 'v' else '') + ' }' + final_return) funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n') # calculate exports @@ -567,12 +577,16 @@ def table_size(table): global_vars = metadata['externs'] else: global_vars = [] # linkable code accesses globals through function calls - global_funcs = list(set([key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2]).difference(set(global_vars)).difference(implemented_functions)) + global_funcs = list(set([key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2]) + .difference(set(global_vars)).difference(implemented_functions)) if settings['RELOCATABLE']: global_funcs += ['g$' + extern for extern in metadata['externs']] side = 'parent' if settings['SIDE_MODULE'] else '' def check(extern): - if settings['ASSERTIONS']: return 'assert(' + side + 'Module["' + extern + '"], "external function \'' + extern + '\' is missing. perhaps a side module was not linked in? if this symbol was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment");' + if settings['ASSERTIONS']: + return ('assert(' + side + 'Module["' + extern + '"], "external function \'' + extern + + '\' is missing. perhaps a side module was not linked in? if this symbol was expected to arrive ' + 'from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment");') return '' for extern in metadata['externs']: asm_setup += 'var g$' + extern + ' = function() { ' + check(extern) + ' return ' + side + 'Module["' + extern + '"] };\n' @@ -581,24 +595,31 @@ def check(extern): bg_funcs = basic_funcs + global_funcs bg_vars = basic_vars + global_vars - asm_global_funcs, asm_global_vars = create_asm_globals(provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings) + asm_global_funcs, asm_global_vars = create_asm_globals( + provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings) the_global = create_the_global(metadata, settings) - sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' + sending_vars = basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars + sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in sending_vars]) + ' }' - receiving = create_receiving(function_table_data, function_tables, function_tables_defs, exported_implemented_functions, settings) + receiving = create_receiving(function_table_data, function_tables, function_tables_defs, + exported_implemented_functions, settings) final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs if settings.get('EMULATED_FUNCTION_POINTERS'): asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' final_function_tables = final_function_tables.replace("asm['", '').replace("']", '').replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_').replace('var dynCall_', '//') - if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs) + (function_tables_defs.count('\n') * len(' ')), len(exports), len(the_global), len(sending), len(receiving)])) - if DEBUG: + logging.debug('asm text sizes' + str([ + map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), + len('\n'.join(function_tables_impls)), len(function_tables_defs) + (function_tables_defs.count('\n') * len(' ')), + len(exports), len(the_global), len(sending), len(receiving)])) logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) - return post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json + return (post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, + asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, + exports, function_table_data, forwarded_json) def memory_and_global_initializers(pre, metadata, mem_init, settings): @@ -1098,8 +1119,9 @@ def create_receiving(function_table_data, function_tables, function_tables_defs, return receiving -def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG): - +def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, + asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, + function_table_data, forwarded_json, settings, outfile, DEBUG): if DEBUG: logging.debug('emscript: python processing: finalize') t = time.time() From cd48558f4011093eed2b3feb182af7f4bc7542d7 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 13:17:27 -0700 Subject: [PATCH 26/52] Extract make_function_tables_impls --- emscripten.py | 148 ++++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 72 deletions(-) diff --git a/emscripten.py b/emscripten.py index 30e7bdde9d0a2..7bc269b844697 100755 --- a/emscripten.py +++ b/emscripten.py @@ -462,79 +462,8 @@ def table_size(table): function_tables = ['dynCall_' + table for table in function_table_data] else: function_tables = [] - function_tables_impls = [] - for sig in function_table_data.iterkeys(): - args = ','.join(['a' + str(i) for i in range(1, len(sig))]) - arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) - coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) - ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings) - if not settings['EMULATED_FUNCTION_POINTERS']: - function_tables_impls.append(''' -function dynCall_%s(index%s%s) { - index = index|0; - %s - %s; -} -''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret)) - else: - function_tables_impls.append(''' -var dynCall_%s = ftCall_%s; -''' % (sig, sig)) - - ffi_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings, ffi_arg=True) for i in range(1, len(sig))]) - for i in range(settings['RESERVED_FUNCTION_POINTERS']): - jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall_%s(%d%s%s)' % (sig, i, ',' if ffi_args else '', ffi_args), sig[0], settings, ffi_result=True) - function_tables_impls.append(''' -function jsCall_%s_%s(%s) { - %s - %s; -} - -''' % (sig, i, args, arg_coercions, jsret)) - shared.Settings.copy(settings) - asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' - basic_funcs.append('invoke_%s' % sig) - if settings.get('RESERVED_FUNCTION_POINTERS'): - asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' - basic_funcs.append('jsCall_%s' % sig) - if settings.get('EMULATED_FUNCTION_POINTERS'): - args = ['a%d' % i for i in range(len(sig)-1)] - full_args = ['x'] + args - table_access = 'FUNCTION_TABLE_' + sig - if settings['SIDE_MODULE']: - table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one - if settings['BINARYEN']: - # wasm uses a Table, which means we have function pointer emulation capabilities all the time, at no cost. just call the table - table_access = "Module['wasmTable']" - table_read = table_access + '.get(x)' - else: - table_read = table_access + '[x]' - prelude = ''' - if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_data, settings)) - asm_setup += ''' -function ftCall_%s(%s) {%s - return %s(%s); -} -''' % (sig, ', '.join(full_args), prelude, table_read, ', '.join(args)) - if not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls - basic_funcs.append('ftCall_%s' % sig) - - if settings.get('RELOCATABLE') and not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls - params = ','.join(['ptr'] + ['p%d' % p for p in range(len(sig)-1)]) - coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i', settings)] + [shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) - coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, unfloat(sig[p+1]), settings)) for p in range(len(sig)-1)]) + ';' - mini_coerced_params = ','.join([shared.JS.make_coercion('p%d', sig[p+1], settings) % p for p in range(len(sig)-1)]) - maybe_return = '' if sig[0] == 'v' else 'return' - final_return = maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', unfloat(sig[0]), settings) + ';' - if settings['EMULATED_FUNCTION_POINTERS'] == 1: - body = final_return - else: - body = ('if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + - shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + - mini_coerced_params + ')', sig[0], settings, ffi_arg=True) + '; ' + - ('return;' if sig[0] == 'v' else '') + ' }' + final_return) - funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n') + function_tables_impls, asm_setup = make_function_tables_impls(asm_setup, basic_funcs, funcs_js, function_table_data, settings) # calculate exports exported_implemented_functions = list(exported_implemented_functions) + metadata['initializers'] @@ -882,6 +811,81 @@ def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1] +def make_function_tables_impls(asm_setup, basic_funcs, funcs_js, function_table_data, settings): + function_tables_impls = [] + for sig in function_table_data.iterkeys(): + args = ','.join(['a' + str(i) for i in range(1, len(sig))]) + arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) + coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) + ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings) + if not settings['EMULATED_FUNCTION_POINTERS']: + function_tables_impls.append(''' +function dynCall_%s(index%s%s) { + index = index|0; + %s + %s; +} +''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret)) + else: + function_tables_impls.append(''' +var dynCall_%s = ftCall_%s; +''' % (sig, sig)) + + ffi_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings, ffi_arg=True) for i in range(1, len(sig))]) + for i in range(settings['RESERVED_FUNCTION_POINTERS']): + jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall_%s(%d%s%s)' % (sig, i, ',' if ffi_args else '', ffi_args), sig[0], settings, ffi_result=True) + function_tables_impls.append(''' +function jsCall_%s_%s(%s) { + %s + %s; +} + +''' % (sig, i, args, arg_coercions, jsret)) + shared.Settings.copy(settings) + asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' + basic_funcs.append('invoke_%s' % sig) + if settings.get('RESERVED_FUNCTION_POINTERS'): + asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' + basic_funcs.append('jsCall_%s' % sig) + if settings.get('EMULATED_FUNCTION_POINTERS'): + args = ['a%d' % i for i in range(len(sig)-1)] + full_args = ['x'] + args + table_access = 'FUNCTION_TABLE_' + sig + if settings['SIDE_MODULE']: + table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one + if settings['BINARYEN']: + # wasm uses a Table, which means we have function pointer emulation capabilities all the time, at no cost. just call the table + table_access = "Module['wasmTable']" + table_read = table_access + '.get(x)' + else: + table_read = table_access + '[x]' + prelude = ''' + if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_data, settings)) + asm_setup += ''' +function ftCall_%s(%s) {%s + return %s(%s); +} +''' % (sig, ', '.join(full_args), prelude, table_read, ', '.join(args)) + if not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls + basic_funcs.append('ftCall_%s' % sig) + + if settings.get('RELOCATABLE') and not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls + params = ','.join(['ptr'] + ['p%d' % p for p in range(len(sig)-1)]) + coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i', settings)] + [shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) + coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, unfloat(sig[p+1]), settings)) for p in range(len(sig)-1)]) + ';' + mini_coerced_params = ','.join([shared.JS.make_coercion('p%d', sig[p+1], settings) % p for p in range(len(sig)-1)]) + maybe_return = '' if sig[0] == 'v' else 'return' + final_return = maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', unfloat(sig[0]), settings) + ';' + if settings['EMULATED_FUNCTION_POINTERS'] == 1: + body = final_return + else: + body = ('if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + + mini_coerced_params + ')', sig[0], settings, ffi_arg=True) + '; ' + + ('return;' if sig[0] == 'v' else '') + ' }' + final_return) + funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n') + return function_tables_impls, asm_setup + def get_function_pointer_error(sig, function_table_data, settings): if settings['ASSERTIONS'] <= 1: extra = ' Module["printErr"]("Build with ASSERTIONS=2 for more info.");' From 2924ceb364dae5fc2d9cc42c0576d3a0e9e09c35 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 13:59:10 -0700 Subject: [PATCH 27/52] Split make_function_tables_impls --- emscripten.py | 57 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/emscripten.py b/emscripten.py index 7bc269b844697..800be855db6b3 100755 --- a/emscripten.py +++ b/emscripten.py @@ -389,6 +389,8 @@ def move_preasm(m): else: pre_tables = '' + function_table_sigs = function_table_data.keys() + in_table, debug_tables, function_tables_defs = make_function_tables_defs( implemented_functions, all_implemented, function_table_data, settings, metadata) @@ -413,9 +415,9 @@ def move_preasm(m): else: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_LOAD_D', 'SAFE_HEAP_STORE', 'SAFE_HEAP_STORE_D', 'SAFE_FT_MASK'] if settings['ASSERTIONS']: - for sig in function_table_data.iterkeys(): + for sig in function_table_sigs: basic_funcs += ['nullFunc_' + sig] - asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_data, settings) + 'abort(x) }\n' + asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_sigs, settings) + 'abort(x) }\n' basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] if not (settings['BINARYEN'] and settings['SIDE_MODULE']): @@ -463,7 +465,10 @@ def table_size(table): else: function_tables = [] - function_tables_impls, asm_setup = make_function_tables_impls(asm_setup, basic_funcs, funcs_js, function_table_data, settings) + function_tables_impls = make_function_tables_impls(function_table_sigs, settings) + asm_setup += setup_function_pointers(function_table_sigs, settings) + basic_funcs += setup_basic_funcs(function_table_sigs, settings) + funcs_js += setup_funcs_js(function_table_sigs, settings) # calculate exports exported_implemented_functions = list(exported_implemented_functions) + metadata['initializers'] @@ -811,9 +816,9 @@ def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1] -def make_function_tables_impls(asm_setup, basic_funcs, funcs_js, function_table_data, settings): +def make_function_tables_impls(function_table_sigs, settings): function_tables_impls = [] - for sig in function_table_data.iterkeys(): + for sig in function_table_sigs: args = ','.join(['a' + str(i) for i in range(1, len(sig))]) arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) @@ -841,12 +846,16 @@ def make_function_tables_impls(asm_setup, basic_funcs, funcs_js, function_table_ } ''' % (sig, i, args, arg_coercions, jsret)) + return function_tables_impls + + +def setup_function_pointers(function_table_sigs, settings): + asm_setup = '' + for sig in function_table_sigs: shared.Settings.copy(settings) asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' - basic_funcs.append('invoke_%s' % sig) if settings.get('RESERVED_FUNCTION_POINTERS'): asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' - basic_funcs.append('jsCall_%s' % sig) if settings.get('EMULATED_FUNCTION_POINTERS'): args = ['a%d' % i for i in range(len(sig)-1)] full_args = ['x'] + args @@ -860,15 +869,33 @@ def make_function_tables_impls(asm_setup, basic_funcs, funcs_js, function_table_ else: table_read = table_access + '[x]' prelude = ''' - if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_data, settings)) + if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_sigs, settings)) asm_setup += ''' function ftCall_%s(%s) {%s return %s(%s); } ''' % (sig, ', '.join(full_args), prelude, table_read, ', '.join(args)) + return asm_setup + + +def setup_basic_funcs(function_table_sigs, settings): + basic_funcs = [] + for sig in function_table_sigs: + shared.Settings.copy(settings) + basic_funcs.append('invoke_%s' % sig) + if settings.get('RESERVED_FUNCTION_POINTERS'): + basic_funcs.append('jsCall_%s' % sig) + if settings.get('EMULATED_FUNCTION_POINTERS'): if not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls basic_funcs.append('ftCall_%s' % sig) + return basic_funcs + +def setup_funcs_js(function_table_sigs, settings): + funcs_js = [] + for sig in function_table_sigs: + shared.Settings.copy(settings) + if settings.get('EMULATED_FUNCTION_POINTERS'): if settings.get('RELOCATABLE') and not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls params = ','.join(['ptr'] + ['p%d' % p for p in range(len(sig)-1)]) coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i', settings)] + [shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) @@ -880,13 +907,15 @@ def make_function_tables_impls(asm_setup, basic_funcs, funcs_js, function_table_ body = final_return else: body = ('if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + - shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + - mini_coerced_params + ')', sig[0], settings, ffi_arg=True) + '; ' + - ('return;' if sig[0] == 'v' else '') + ' }' + final_return) + shared.JS.make_coercion( + 'FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + + mini_coerced_params + ')', sig[0], settings, ffi_arg=True + ) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + final_return) funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n') - return function_tables_impls, asm_setup + return funcs_js + -def get_function_pointer_error(sig, function_table_data, settings): +def get_function_pointer_error(sig, function_table_sigs, settings): if settings['ASSERTIONS'] <= 1: extra = ' Module["printErr"]("Build with ASSERTIONS=2 for more info.");' pointer = ' ' @@ -894,7 +923,7 @@ def get_function_pointer_error(sig, function_table_data, settings): pointer = ' \'" + x + "\' ' extra = ' Module["printErr"]("This pointer might make sense in another type signature: ' # sort signatures, attempting to show most likely related ones first - sigs = function_table_data.keys() + sigs = list(function_table_sigs) sigs.sort(key=signature_sort_key(sig)) for other in sigs: if other != sig: From 47454f8f8431fad042a111ab73b7fc991f3cb11c Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 14:19:11 -0700 Subject: [PATCH 28/52] Extract create_exports, need_asyncify --- emscripten.py | 61 ++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/emscripten.py b/emscripten.py index 800be855db6b3..7fe4f8f1e45e6 100755 --- a/emscripten.py +++ b/emscripten.py @@ -97,12 +97,12 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) with ToolchainProfiler.profile_block('function_tables_and_exports'): - (post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, + (post, funcs_js, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json) = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) with ToolchainProfiler.profile_block('finalize_output'): - finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, + finalize_output(metadata, post, funcs_js, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG) @@ -455,8 +455,7 @@ def table_size(table): # See if we need ASYNCIFY functions # We might not need them even if ASYNCIFY is enabled - need_asyncify = '_emscripten_alloc_async_context' in exported_implemented_functions - if need_asyncify: + if need_asyncify(exported_implemented_functions): basic_vars += ['___async', '___async_unwind', '___async_retval', '___async_cur_frame'] # function tables @@ -484,24 +483,9 @@ def table_size(table): exported_implemented_functions += ['setTempRet0', 'getTempRet0'] if not (settings['BINARYEN'] and settings['SIDE_MODULE']): exported_implemented_functions += ['setThrew'] - - asm_runtime_funcs = create_asm_runtime_funcs(need_asyncify, settings) - - all_exported = exported_implemented_functions + asm_runtime_funcs + function_tables exported_implemented_functions = list(set(exported_implemented_functions)) - if settings['EMULATED_FUNCTION_POINTERS']: - all_exported = list(set(all_exported).union(in_table)) - exports = [] - for export in all_exported: - exports.append(quote(export) + ": " + export) - if settings['BINARYEN'] and settings['SIDE_MODULE']: - # named globals in side wasm modules are exported globals from asm/wasm - for k, v in metadata['namedGlobals'].iteritems(): - exports.append(quote('_' + str(k)) + ': ' + str(v)) - # aliases become additional exports - for k, v in metadata['aliases'].iteritems(): - exports.append(quote(str(k)) + ': ' + str(v)) - exports = '{ ' + ', '.join(exports) + ' }' + + exports = create_exports(exported_implemented_functions, in_table, function_tables, settings) # calculate globals try: del forwarded_json['Variables']['globals']['_llvm_global_ctors'] # not a true variable @@ -551,7 +535,7 @@ def check(extern): len(exports), len(the_global), len(sending), len(receiving)])) logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) - return (post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, + return (post, funcs_js, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json) @@ -1086,7 +1070,32 @@ def make_simd_types(metadata, settings): } -def create_asm_runtime_funcs(need_asyncify, settings): +def need_asyncify(exported_implemented_functions): + return '_emscripten_alloc_async_context' in exported_implemented_functions + + +def create_exports(exported_implemented_functions, in_table, function_tables, settings): + quote = quoter(settings) + asm_runtime_funcs = create_asm_runtime_funcs(settings) + if need_asyncify(exported_implemented_functions): + asm_runtime_funcs.append('setAsync') + all_exported = exported_implemented_functions + asm_runtime_funcs + function_tables + if settings['EMULATED_FUNCTION_POINTERS']: + all_exported = list(set(all_exported).union(in_table)) + exports = [] + for export in all_exported: + exports.append(quote(export) + ": " + export) + if settings['BINARYEN'] and settings['SIDE_MODULE']: + # named globals in side wasm modules are exported globals from asm/wasm + for k, v in metadata['namedGlobals'].iteritems(): + exports.append(quote('_' + str(k)) + ': ' + str(v)) + # aliases become additional exports + for k, v in metadata['aliases'].iteritems(): + exports.append(quote(str(k)) + ': ' + str(v)) + return '{ ' + ', '.join(exports) + ' }' + + +def create_asm_runtime_funcs(settings): funcs = [] if not (settings['BINARYEN'] and settings['SIDE_MODULE']): funcs += ['stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', 'setThrew'] @@ -1100,8 +1109,6 @@ def create_asm_runtime_funcs(need_asyncify, settings): funcs += ['emterpret'] if settings.get('EMTERPRETIFY_ASYNC'): funcs += ['setAsyncState', 'emtStackSave', 'emtStackRestore'] - if need_asyncify: - funcs += ['setAsync'] return funcs @@ -1152,7 +1159,7 @@ def create_receiving(function_table_data, function_tables, function_tables_defs, return receiving -def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm_safe_heap, sending, receiving, +def finalize_output(metadata, post, funcs_js, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG): if DEBUG: @@ -1295,7 +1302,7 @@ def finalize_output(metadata, post, funcs_js, need_asyncify, provide_fround, asm ''' + (''' function setAsync() { ___async = 1; -}''' if need_asyncify else '') + (''' +}''' if need_asyncify(exports) else '') + (''' function emterpret(pc) { // this will be replaced when the emterpreter code is generated; adding it here allows validation until then pc = pc | 0; assert(0); From 6ef0fae20a22833ddcda4d8cd681a4fdafc0c5bd Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 15:47:48 -0700 Subject: [PATCH 29/52] Fix missing metadata argument for create_exports --- emscripten.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emscripten.py b/emscripten.py index 7fe4f8f1e45e6..4866cd383c4c0 100755 --- a/emscripten.py +++ b/emscripten.py @@ -485,7 +485,7 @@ def table_size(table): exported_implemented_functions += ['setThrew'] exported_implemented_functions = list(set(exported_implemented_functions)) - exports = create_exports(exported_implemented_functions, in_table, function_tables, settings) + exports = create_exports(exported_implemented_functions, in_table, function_tables, metadata, settings) # calculate globals try: del forwarded_json['Variables']['globals']['_llvm_global_ctors'] # not a true variable @@ -1074,7 +1074,7 @@ def need_asyncify(exported_implemented_functions): return '_emscripten_alloc_async_context' in exported_implemented_functions -def create_exports(exported_implemented_functions, in_table, function_tables, settings): +def create_exports(exported_implemented_functions, in_table, function_tables, metadata, settings): quote = quoter(settings) asm_runtime_funcs = create_asm_runtime_funcs(settings) if need_asyncify(exported_implemented_functions): From d084be739184f5abc95e33c40942298604e93643 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 16:04:40 -0700 Subject: [PATCH 30/52] Extract asm_safe_heap --- emscripten.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/emscripten.py b/emscripten.py index 4866cd383c4c0..3ddb6cf127c2a 100755 --- a/emscripten.py +++ b/emscripten.py @@ -97,12 +97,12 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) with ToolchainProfiler.profile_block('function_tables_and_exports'): - (post, funcs_js, provide_fround, asm_safe_heap, sending, receiving, asm_setup, the_global, + (post, funcs_js, provide_fround, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json) = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) with ToolchainProfiler.profile_block('finalize_output'): - finalize_output(metadata, post, funcs_js, provide_fround, asm_safe_heap, sending, + finalize_output(metadata, post, funcs_js, provide_fround, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG) @@ -407,10 +407,8 @@ def move_preasm(m): if settings['ABORTING_MALLOC']: basic_funcs += ['abortOnCannotGrowMemory'] if settings['STACK_OVERFLOW_CHECK']: basic_funcs += ['abortStackOverflow'] - asm_safe_heap = settings['SAFE_HEAP'] and not settings['SAFE_HEAP_LOG'] and not settings['RELOCATABLE'] # optimized safe heap in asm, when we can - if settings['SAFE_HEAP']: - if asm_safe_heap: + if asm_safe_heap(settings): basic_funcs += ['segfault', 'alignfault', 'ftfault'] else: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_LOAD_D', 'SAFE_HEAP_STORE', 'SAFE_HEAP_STORE_D', 'SAFE_FT_MASK'] @@ -535,7 +533,7 @@ def check(extern): len(exports), len(the_global), len(sending), len(receiving)])) logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) - return (post, funcs_js, provide_fround, asm_safe_heap, sending, receiving, + return (post, funcs_js, provide_fround, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json) @@ -1074,6 +1072,11 @@ def need_asyncify(exported_implemented_functions): return '_emscripten_alloc_async_context' in exported_implemented_functions +def asm_safe_heap(settings): + """optimized safe heap in asm, when we can""" + settings['SAFE_HEAP'] and not settings['SAFE_HEAP_LOG'] and not settings['RELOCATABLE'] + + def create_exports(exported_implemented_functions, in_table, function_tables, metadata, settings): quote = quoter(settings) asm_runtime_funcs = create_asm_runtime_funcs(settings) @@ -1159,7 +1162,7 @@ def create_receiving(function_table_data, function_tables, function_tables_defs, return receiving -def finalize_output(metadata, post, funcs_js, provide_fround, asm_safe_heap, sending, receiving, +def finalize_output(metadata, post, funcs_js, provide_fround, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG): if DEBUG: @@ -1333,7 +1336,7 @@ def finalize_output(metadata, post, funcs_js, provide_fround, asm_safe_heap, sen value = value | 0; HEAP32[DYNAMICTOP_PTR>>2] = value; } -'''] + ['' if not asm_safe_heap else ''' +'''] + ['' if not asm_safe_heap(settings) else ''' function SAFE_HEAP_STORE(dest, value, bytes) { dest = dest | 0; value = value | 0; From a9b71e6dcdcfda0dff961bbad0a89f0b8e161f0f Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 16:12:04 -0700 Subject: [PATCH 31/52] Remove unused var basic_float_vars --- emscripten.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/emscripten.py b/emscripten.py index 3ddb6cf127c2a..eb416c0ae22e9 100755 --- a/emscripten.py +++ b/emscripten.py @@ -420,7 +420,6 @@ def move_preasm(m): basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] if not (settings['BINARYEN'] and settings['SIDE_MODULE']): basic_vars += ['STACKTOP', 'STACK_MAX'] - basic_float_vars = [] if metadata.get('preciseI64MathUsed'): basic_vars += ['cttz_i8'] @@ -515,7 +514,7 @@ def check(extern): provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings) the_global = create_the_global(metadata, settings) - sending_vars = basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars + sending_vars = basic_funcs + global_funcs + basic_vars + global_vars sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in sending_vars]) + ' }' receiving = create_receiving(function_table_data, function_tables, function_tables_defs, @@ -1845,7 +1844,6 @@ def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1] basic_vars = ['STACKTOP', 'STACK_MAX', 'DYNAMICTOP_PTR', 'ABORT'] - basic_float_vars = [] # Asm.js-style exception handling: invoke wrapper generation invoke_wrappers = '' @@ -1861,7 +1859,7 @@ def math_fix(g): # sent data the_global = '{}' - sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' + sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + global_vars]) + ' }' # received receiving = '' if settings['ASSERTIONS']: From 2baf1353cdf17f6a05fae2a5375bbf9d868a4ac7 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 16:27:09 -0700 Subject: [PATCH 32/52] Split get_exported_implemented_functions to only return exported_implemented_functions --- emscripten.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/emscripten.py b/emscripten.py index eb416c0ae22e9..db76519cf3b55 100755 --- a/emscripten.py +++ b/emscripten.py @@ -359,14 +359,19 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, function_table_data = metadata['tables'] + # merge forwarded data + settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] + pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') #print >> sys.stderr, 'glue:', pre, '\n\n||||||||||||||||\n\n', post, '...............' pre = memory_and_global_initializers(pre, metadata, mem_init, settings) pre, funcs_js = get_js_funcs(pre, funcs) - exported_implemented_functions, all_implemented = get_exported_implemented_functions( - metadata, function_table_data, forwarded_json, settings) + all_exported_functions = get_all_exported_functions(function_table_data, settings) + all_implemented = get_all_implemented(forwarded_json, metadata) + exported_implemented_functions = get_exported_implemented_functions( + all_exported_functions, all_implemented, metadata, settings) implemented_functions = get_implemented_functions(pre, metadata, settings, all_implemented) pre = include_asm_consts(pre, forwarded_json, metadata, settings) #if DEBUG: outfile.write('// pre\n') @@ -570,9 +575,7 @@ def get_js_funcs(pre, funcs): return pre, funcs_js -def get_exported_implemented_functions(metadata, function_table_data, forwarded_json, settings): - # merge forwarded data - settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] +def get_all_exported_functions(function_table_data, settings): all_exported_functions = set(shared.expand_response(settings['EXPORTED_FUNCTIONS'])) # both asm.js and otherwise for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented @@ -582,14 +585,21 @@ def get_exported_implemented_functions(metadata, function_table_data, forwarded_ for func in table.split('[')[1].split(']')[0].split(','): if func[0] == '_': all_exported_functions.add(func) + return all_exported_functions + + +def get_all_implemented(forwarded_json, metadata): + return metadata['implementedFunctions'] + forwarded_json['Functions']['implementedFunctions'].keys() # XXX perf? + + +def get_exported_implemented_functions(all_exported_functions, all_implemented, metadata, settings): exported_implemented_functions = set(metadata['exports']) export_bindings = settings['EXPORT_BINDINGS'] export_all = settings['EXPORT_ALL'] - all_implemented = metadata['implementedFunctions'] + forwarded_json['Functions']['implementedFunctions'].keys() # XXX perf? for key in all_implemented: if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): exported_implemented_functions.add(key) - return exported_implemented_functions, all_implemented + return exported_implemented_functions def get_implemented_functions(pre, metadata, settings, all_implemented): From 35fad6e5e1c60aa2c893dfb4240b77ca59477bec Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 16:30:02 -0700 Subject: [PATCH 33/52] Move all the initialization for exported_implemented_functions into its builder function --- emscripten.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/emscripten.py b/emscripten.py index db76519cf3b55..91e345db30fa3 100755 --- a/emscripten.py +++ b/emscripten.py @@ -370,8 +370,6 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, pre, funcs_js = get_js_funcs(pre, funcs) all_exported_functions = get_all_exported_functions(function_table_data, settings) all_implemented = get_all_implemented(forwarded_json, metadata) - exported_implemented_functions = get_exported_implemented_functions( - all_exported_functions, all_implemented, metadata, settings) implemented_functions = get_implemented_functions(pre, metadata, settings, all_implemented) pre = include_asm_consts(pre, forwarded_json, metadata, settings) #if DEBUG: outfile.write('// pre\n') @@ -455,6 +453,9 @@ def table_size(table): if not settings['EMULATED_FUNCTION_POINTERS']: asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size + exported_implemented_functions = get_exported_implemented_functions( + all_exported_functions, all_implemented, metadata, settings) + # See if we need ASYNCIFY functions # We might not need them even if ASYNCIFY is enabled if need_asyncify(exported_implemented_functions): @@ -472,21 +473,6 @@ def table_size(table): funcs_js += setup_funcs_js(function_table_sigs, settings) # calculate exports - exported_implemented_functions = list(exported_implemented_functions) + metadata['initializers'] - if not settings['ONLY_MY_CODE']: - exported_implemented_functions.append('runPostSets') - if settings['ALLOW_MEMORY_GROWTH']: - exported_implemented_functions.append('_emscripten_replace_memory') - if not settings['SIDE_MODULE']: - exported_implemented_functions += ['stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace'] - if settings['SAFE_HEAP']: - exported_implemented_functions += ['setDynamicTop'] - if not settings['RELOCATABLE']: - exported_implemented_functions += ['setTempRet0', 'getTempRet0'] - if not (settings['BINARYEN'] and settings['SIDE_MODULE']): - exported_implemented_functions += ['setThrew'] - exported_implemented_functions = list(set(exported_implemented_functions)) - exports = create_exports(exported_implemented_functions, in_table, function_tables, metadata, settings) # calculate globals try: @@ -599,6 +585,21 @@ def get_exported_implemented_functions(all_exported_functions, all_implemented, for key in all_implemented: if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): exported_implemented_functions.add(key) + + exported_implemented_functions = list(exported_implemented_functions) + metadata['initializers'] + if not settings['ONLY_MY_CODE']: + exported_implemented_functions.append('runPostSets') + if settings['ALLOW_MEMORY_GROWTH']: + exported_implemented_functions.append('_emscripten_replace_memory') + if not settings['SIDE_MODULE']: + exported_implemented_functions += ['stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace'] + if settings['SAFE_HEAP']: + exported_implemented_functions += ['setDynamicTop'] + if not settings['RELOCATABLE']: + exported_implemented_functions += ['setTempRet0', 'getTempRet0'] + if not (settings['BINARYEN'] and settings['SIDE_MODULE']): + exported_implemented_functions += ['setThrew'] + exported_implemented_functions = list(set(exported_implemented_functions)) return exported_implemented_functions From e5af478380d867c94bca0664541fe2a1db692eea Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Thu, 13 Apr 2017 16:53:34 -0700 Subject: [PATCH 34/52] Extract function_tables --- emscripten.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/emscripten.py b/emscripten.py index 91e345db30fa3..5a273c8b4ba08 100755 --- a/emscripten.py +++ b/emscripten.py @@ -461,19 +461,13 @@ def table_size(table): if need_asyncify(exported_implemented_functions): basic_vars += ['___async', '___async_unwind', '___async_retval', '___async_cur_frame'] - # function tables - if not settings['EMULATED_FUNCTION_POINTERS']: - function_tables = ['dynCall_' + table for table in function_table_data] - else: - function_tables = [] - function_tables_impls = make_function_tables_impls(function_table_sigs, settings) asm_setup += setup_function_pointers(function_table_sigs, settings) basic_funcs += setup_basic_funcs(function_table_sigs, settings) funcs_js += setup_funcs_js(function_table_sigs, settings) - # calculate exports - exports = create_exports(exported_implemented_functions, in_table, function_tables, metadata, settings) + exports = create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings) + # calculate globals try: del forwarded_json['Variables']['globals']['_llvm_global_ctors'] # not a true variable @@ -508,7 +502,7 @@ def check(extern): sending_vars = basic_funcs + global_funcs + basic_vars + global_vars sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in sending_vars]) + ' }' - receiving = create_receiving(function_table_data, function_tables, function_tables_defs, + receiving = create_receiving(function_table_data, function_tables_defs, exported_implemented_functions, settings) final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs @@ -1087,12 +1081,12 @@ def asm_safe_heap(settings): settings['SAFE_HEAP'] and not settings['SAFE_HEAP_LOG'] and not settings['RELOCATABLE'] -def create_exports(exported_implemented_functions, in_table, function_tables, metadata, settings): +def create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings): quote = quoter(settings) asm_runtime_funcs = create_asm_runtime_funcs(settings) if need_asyncify(exported_implemented_functions): asm_runtime_funcs.append('setAsync') - all_exported = exported_implemented_functions + asm_runtime_funcs + function_tables + all_exported = exported_implemented_functions + asm_runtime_funcs + function_tables(function_table_data, settings) if settings['EMULATED_FUNCTION_POINTERS']: all_exported = list(set(all_exported).union(in_table)) exports = [] @@ -1125,6 +1119,13 @@ def create_asm_runtime_funcs(settings): return funcs +def function_tables(function_table_data, settings): + if not settings['EMULATED_FUNCTION_POINTERS']: + return ['dynCall_' + table for table in function_table_data] + else: + return [] + + def create_the_global(metadata, settings): fundamentals = ['Math'] fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] @@ -1137,7 +1138,7 @@ def create_the_global(metadata, settings): return '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' -def create_receiving(function_table_data, function_tables, function_tables_defs, exported_implemented_functions, settings): +def create_receiving(function_table_data, function_tables_defs, exported_implemented_functions, settings): receiving = '' if settings['ASSERTIONS']: # assert on the runtime being in a valid state when calling into compiled code. The only exceptions are @@ -1150,7 +1151,7 @@ def create_receiving(function_table_data, function_tables, function_tables_defs, ''' for s in exported_implemented_functions if s not in ['_memcpy', '_memset', 'runPostSets', '_emscripten_replace_memory', '__start_module']]) if not settings['SWAPPABLE_ASM_MODULE']: - receiving += ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables]) + receiving += ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables(function_table_data, settings)]) else: receiving += 'Module["asm"] = asm;\n' + ';\n'.join(['var ' + s + ' = Module["' + s + '"] = function() { return Module["asm"]["' + s + '"].apply(null, arguments) }' for s in exported_implemented_functions + function_tables]) receiving += ';\n' From 8ca8aac4e8be9d96ebd023372181c20a45f603e9 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 08:59:40 -0700 Subject: [PATCH 35/52] Remove unused math_envs var --- emscripten.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/emscripten.py b/emscripten.py index 5a273c8b4ba08..5b4ed57bb7568 100755 --- a/emscripten.py +++ b/emscripten.py @@ -403,10 +403,7 @@ def move_preasm(m): for sig in function_table_data: asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' - math_envs = [] - - - basic_funcs = ['abort', 'assert', 'enlargeMemory', 'getTotalMemory'] + [m.replace('.', '_') for m in math_envs] + basic_funcs = ['abort', 'assert', 'enlargeMemory', 'getTotalMemory'] if settings['ABORTING_MALLOC']: basic_funcs += ['abortOnCannotGrowMemory'] if settings['STACK_OVERFLOW_CHECK']: basic_funcs += ['abortStackOverflow'] From e2d9208a0cb551c8c10003e7e016c99ba04c9261 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 09:05:53 -0700 Subject: [PATCH 36/52] Add missing return statement --- emscripten.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emscripten.py b/emscripten.py index 5b4ed57bb7568..8677432c9dbca 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1075,7 +1075,7 @@ def need_asyncify(exported_implemented_functions): def asm_safe_heap(settings): """optimized safe heap in asm, when we can""" - settings['SAFE_HEAP'] and not settings['SAFE_HEAP_LOG'] and not settings['RELOCATABLE'] + return settings['SAFE_HEAP'] and not settings['SAFE_HEAP_LOG'] and not settings['RELOCATABLE'] def create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings): From 3a7fa070668d32d21caac248beb32c95f688f054 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 09:10:46 -0700 Subject: [PATCH 37/52] Extract provide_fround --- emscripten.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/emscripten.py b/emscripten.py index 8677432c9dbca..4f6443052bf40 100755 --- a/emscripten.py +++ b/emscripten.py @@ -97,12 +97,12 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) with ToolchainProfiler.profile_block('function_tables_and_exports'): - (post, funcs_js, provide_fround, sending, receiving, asm_setup, the_global, + (post, funcs_js, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json) = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) with ToolchainProfiler.profile_block('finalize_output'): - finalize_output(metadata, post, funcs_js, provide_fround, sending, + finalize_output(metadata, post, funcs_js, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG) @@ -488,12 +488,10 @@ def check(extern): for extern in metadata['externs']: asm_setup += 'var g$' + extern + ' = function() { ' + check(extern) + ' return ' + side + 'Module["' + extern + '"] };\n' - provide_fround = settings['PRECISE_F32'] or settings['SIMD'] - bg_funcs = basic_funcs + global_funcs bg_vars = basic_vars + global_vars asm_global_funcs, asm_global_vars = create_asm_globals( - provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings) + bg_funcs, bg_vars, access_quote, metadata, settings) the_global = create_the_global(metadata, settings) sending_vars = basic_funcs + global_funcs + basic_vars + global_vars @@ -514,7 +512,7 @@ def check(extern): len(exports), len(the_global), len(sending), len(receiving)])) logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) - return (post, funcs_js, provide_fround, sending, receiving, + return (post, funcs_js, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json) @@ -934,9 +932,9 @@ def closure(other): return closure -def create_asm_globals(provide_fround, bg_funcs, bg_vars, access_quote, metadata, settings): +def create_asm_globals(bg_funcs, bg_vars, access_quote, metadata, settings): maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] - if provide_fround: + if provide_fround(settings): maths += ['Math.fround'] asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global' + access_quote(g) + ';\n' for g in maths]) @@ -1078,6 +1076,9 @@ def asm_safe_heap(settings): return settings['SAFE_HEAP'] and not settings['SAFE_HEAP_LOG'] and not settings['RELOCATABLE'] +def provide_fround(settings): + return settings['PRECISE_F32'] or settings['SIMD'] + def create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings): quote = quoter(settings) asm_runtime_funcs = create_asm_runtime_funcs(settings) @@ -1170,7 +1171,7 @@ def create_receiving(function_table_data, function_tables_defs, exported_impleme return receiving -def finalize_output(metadata, post, funcs_js, provide_fround, sending, receiving, +def finalize_output(metadata, post, funcs_js, sending, receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG): if DEBUG: @@ -1492,9 +1493,9 @@ def finalize_output(metadata, post, funcs_js, provide_fround, sending, receiving var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0; var tempRet0 = 0; ''' % (access_quote('NaN'), access_quote('Infinity'))) + '\n' + asm_global_funcs] + \ - [' var tempFloat = %s;\n' % ('Math_fround(0)' if provide_fround else '0.0')] + \ + [' var tempFloat = %s;\n' % ('Math_fround(0)' if provide_fround(settings) else '0.0')] + \ [' var asyncState = 0;\n' if settings.get('EMTERPRETIFY_ASYNC') else ''] + \ - ([' const f0 = Math_fround(0);\n'] if provide_fround else []) + \ + ([' const f0 = Math_fround(0);\n'] if provide_fround(settings) else []) + \ ['' if not settings['ALLOW_MEMORY_GROWTH'] else ''' function _emscripten_replace_memory(newBuffer) { if ((byteLength(newBuffer) & 0xffffff || byteLength(newBuffer) <= 0xffffff) || byteLength(newBuffer) > 0x80000000) return false; From a3286f898bd73a71a7b81b2babb128639e7af72f Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 09:21:46 -0700 Subject: [PATCH 38/52] Group asm_setup, basic_vars, and basic_funcs --- emscripten.py | 56 ++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/emscripten.py b/emscripten.py index 4f6443052bf40..83aa6abf12b35 100755 --- a/emscripten.py +++ b/emscripten.py @@ -397,16 +397,38 @@ def move_preasm(m): in_table, debug_tables, function_tables_defs = make_function_tables_defs( implemented_functions, all_implemented, function_table_data, settings, metadata) - asm_setup = '' + exported_implemented_functions = get_exported_implemented_functions( + all_exported_functions, all_implemented, metadata, settings) + asm_setup = '' if settings['ASSERTIONS'] >= 2: for sig in function_table_data: asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' + if settings['ASSERTIONS']: + for sig in function_table_sigs: + asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_sigs, settings) + 'abort(x) }\n' + if settings['RELOCATABLE']: + asm_setup += 'var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;\n' + if not settings['SIDE_MODULE']: + asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n' + if settings['BINARYEN']: + def table_size(table): + table_contents = table[table.index('[') + 1: table.index(']')] + if len(table_contents) == 0: # empty table + return 0 + return table_contents.count(',') + 1 - basic_funcs = ['abort', 'assert', 'enlargeMemory', 'getTotalMemory'] - if settings['ABORTING_MALLOC']: basic_funcs += ['abortOnCannotGrowMemory'] - if settings['STACK_OVERFLOW_CHECK']: basic_funcs += ['abortStackOverflow'] + table_total_size = sum(map(table_size, function_table_data.values())) + asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size + if not settings['EMULATED_FUNCTION_POINTERS']: + asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size + + basic_funcs = ['abort', 'assert', 'enlargeMemory', 'getTotalMemory'] + if settings['ABORTING_MALLOC']: + basic_funcs += ['abortOnCannotGrowMemory'] + if settings['STACK_OVERFLOW_CHECK']: + basic_funcs += ['abortStackOverflow'] if settings['SAFE_HEAP']: if asm_safe_heap(settings): basic_funcs += ['segfault', 'alignfault', 'ftfault'] @@ -415,43 +437,22 @@ def move_preasm(m): if settings['ASSERTIONS']: for sig in function_table_sigs: basic_funcs += ['nullFunc_' + sig] - asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_sigs, settings) + 'abort(x) }\n' + if settings['RELOCATABLE']: + basic_funcs += ['setTempRet0', 'getTempRet0'] basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] if not (settings['BINARYEN'] and settings['SIDE_MODULE']): basic_vars += ['STACKTOP', 'STACK_MAX'] - if metadata.get('preciseI64MathUsed'): basic_vars += ['cttz_i8'] else: if forwarded_json['Functions']['libraryFunctions'].get('_llvm_cttz_i32'): basic_vars += ['cttz_i8'] - if settings['RELOCATABLE']: if not (settings['BINARYEN'] and settings['SIDE_MODULE']): basic_vars += ['gb', 'fb'] else: basic_vars += ['memoryBase', 'tableBase'] # wasm side modules have a specific convention for these - if not settings['SIDE_MODULE']: - asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n' - - basic_funcs += ['setTempRet0', 'getTempRet0'] - asm_setup += 'var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;\n' - - if settings['BINARYEN']: - def table_size(table): - table_contents = table[table.index('[') + 1: table.index(']')] - if len(table_contents) == 0: # empty table - return 0 - return table_contents.count(',') + 1 - - table_total_size = sum(map(table_size, function_table_data.values())) - asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size - if not settings['EMULATED_FUNCTION_POINTERS']: - asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size - - exported_implemented_functions = get_exported_implemented_functions( - all_exported_functions, all_implemented, metadata, settings) # See if we need ASYNCIFY functions # We might not need them even if ASYNCIFY is enabled @@ -1079,6 +1080,7 @@ def asm_safe_heap(settings): def provide_fround(settings): return settings['PRECISE_F32'] or settings['SIMD'] + def create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings): quote = quoter(settings) asm_runtime_funcs = create_asm_runtime_funcs(settings) From 414d9e3c85d48075a4a13bad2cbedb6d3062469e Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 09:22:03 -0700 Subject: [PATCH 39/52] Fix function_tables extract for binaryen --- emscripten.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emscripten.py b/emscripten.py index 83aa6abf12b35..7e68bae34acd7 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1153,7 +1153,7 @@ def create_receiving(function_table_data, function_tables_defs, exported_impleme if not settings['SWAPPABLE_ASM_MODULE']: receiving += ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables(function_table_data, settings)]) else: - receiving += 'Module["asm"] = asm;\n' + ';\n'.join(['var ' + s + ' = Module["' + s + '"] = function() { return Module["asm"]["' + s + '"].apply(null, arguments) }' for s in exported_implemented_functions + function_tables]) + receiving += 'Module["asm"] = asm;\n' + ';\n'.join(['var ' + s + ' = Module["' + s + '"] = function() { return Module["asm"]["' + s + '"].apply(null, arguments) }' for s in exported_implemented_functions + function_tables(function_table_data, settings)]) receiving += ';\n' if settings['EXPORT_FUNCTION_TABLES'] and not settings['BINARYEN']: From 15f3324305a3bf0011549105863b76e47e80ca9e Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 09:45:13 -0700 Subject: [PATCH 40/52] Extract creation functions for asm_setup, basic_funcs, basic_vars --- emscripten.py | 117 +++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 53 deletions(-) diff --git a/emscripten.py b/emscripten.py index 7e68bae34acd7..885483a966344 100755 --- a/emscripten.py +++ b/emscripten.py @@ -400,59 +400,9 @@ def move_preasm(m): exported_implemented_functions = get_exported_implemented_functions( all_exported_functions, all_implemented, metadata, settings) - asm_setup = '' - if settings['ASSERTIONS'] >= 2: - for sig in function_table_data: - asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' - if settings['ASSERTIONS']: - for sig in function_table_sigs: - asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_sigs, settings) + 'abort(x) }\n' - if settings['RELOCATABLE']: - asm_setup += 'var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;\n' - if not settings['SIDE_MODULE']: - asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n' - if settings['BINARYEN']: - def table_size(table): - table_contents = table[table.index('[') + 1: table.index(']')] - if len(table_contents) == 0: # empty table - return 0 - return table_contents.count(',') + 1 - - table_total_size = sum(map(table_size, function_table_data.values())) - asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size - if not settings['EMULATED_FUNCTION_POINTERS']: - asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size - - - basic_funcs = ['abort', 'assert', 'enlargeMemory', 'getTotalMemory'] - if settings['ABORTING_MALLOC']: - basic_funcs += ['abortOnCannotGrowMemory'] - if settings['STACK_OVERFLOW_CHECK']: - basic_funcs += ['abortStackOverflow'] - if settings['SAFE_HEAP']: - if asm_safe_heap(settings): - basic_funcs += ['segfault', 'alignfault', 'ftfault'] - else: - basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_LOAD_D', 'SAFE_HEAP_STORE', 'SAFE_HEAP_STORE_D', 'SAFE_FT_MASK'] - if settings['ASSERTIONS']: - for sig in function_table_sigs: - basic_funcs += ['nullFunc_' + sig] - if settings['RELOCATABLE']: - basic_funcs += ['setTempRet0', 'getTempRet0'] - - basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] - if not (settings['BINARYEN'] and settings['SIDE_MODULE']): - basic_vars += ['STACKTOP', 'STACK_MAX'] - if metadata.get('preciseI64MathUsed'): - basic_vars += ['cttz_i8'] - else: - if forwarded_json['Functions']['libraryFunctions'].get('_llvm_cttz_i32'): - basic_vars += ['cttz_i8'] - if settings['RELOCATABLE']: - if not (settings['BINARYEN'] and settings['SIDE_MODULE']): - basic_vars += ['gb', 'fb'] - else: - basic_vars += ['memoryBase', 'tableBase'] # wasm side modules have a specific convention for these + asm_setup = create_asm_setup(debug_tables, function_table_data, settings) + basic_funcs = create_basic_funcs(function_table_sigs, settings) + basic_vars = create_basic_vars(forwarded_json, metadata, settings) # See if we need ASYNCIFY functions # We might not need them even if ASYNCIFY is enabled @@ -1081,6 +1031,67 @@ def provide_fround(settings): return settings['PRECISE_F32'] or settings['SIMD'] +def create_asm_setup(debug_tables, function_table_data, settings): + asm_setup = '' + if settings['ASSERTIONS'] >= 2: + for sig in function_table_data: + asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' + if settings['ASSERTIONS']: + function_table_sigs = function_table_data.keys() + for sig in function_table_sigs: + asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_sigs, settings) + 'abort(x) }\n' + if settings['RELOCATABLE']: + asm_setup += 'var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;\n' + if not settings['SIDE_MODULE']: + asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n' + if settings['BINARYEN']: + def table_size(table): + table_contents = table[table.index('[') + 1: table.index(']')] + if len(table_contents) == 0: # empty table + return 0 + return table_contents.count(',') + 1 + + table_total_size = sum(map(table_size, function_table_data.values())) + asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size + if not settings['EMULATED_FUNCTION_POINTERS']: + asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size + return asm_setup + + +def create_basic_funcs(function_table_sigs, settings): + basic_funcs = ['abort', 'assert', 'enlargeMemory', 'getTotalMemory'] + if settings['ABORTING_MALLOC']: + basic_funcs += ['abortOnCannotGrowMemory'] + if settings['STACK_OVERFLOW_CHECK']: + basic_funcs += ['abortStackOverflow'] + if settings['SAFE_HEAP']: + if asm_safe_heap(settings): + basic_funcs += ['segfault', 'alignfault', 'ftfault'] + else: + basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_LOAD_D', 'SAFE_HEAP_STORE', 'SAFE_HEAP_STORE_D', 'SAFE_FT_MASK'] + if settings['ASSERTIONS']: + for sig in function_table_sigs: + basic_funcs += ['nullFunc_' + sig] + if settings['RELOCATABLE']: + basic_funcs += ['setTempRet0', 'getTempRet0'] + return basic_funcs + +def create_basic_vars(forwarded_json, metadata, settings): + basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] + if not (settings['BINARYEN'] and settings['SIDE_MODULE']): + basic_vars += ['STACKTOP', 'STACK_MAX'] + if metadata.get('preciseI64MathUsed'): + basic_vars += ['cttz_i8'] + else: + if forwarded_json['Functions']['libraryFunctions'].get('_llvm_cttz_i32'): + basic_vars += ['cttz_i8'] + if settings['RELOCATABLE']: + if not (settings['BINARYEN'] and settings['SIDE_MODULE']): + basic_vars += ['gb', 'fb'] + else: + basic_vars += ['memoryBase', 'tableBase'] # wasm side modules have a specific convention for these + return basic_vars + def create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings): quote = quoter(settings) asm_runtime_funcs = create_asm_runtime_funcs(settings) From f57557b3768e91f5428e21c019d40e6bb74a0372 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 10:02:03 -0700 Subject: [PATCH 41/52] Split asm_global_funcs and _vars, remove unused quoters --- emscripten.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/emscripten.py b/emscripten.py index 885483a966344..2141380ec8f4f 100755 --- a/emscripten.py +++ b/emscripten.py @@ -350,9 +350,6 @@ def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, logging.debug('emscript: python processing: function tables and exports') t = time.time() - access_quote = access_quoter(settings) - quote = quoter(settings) - forwarded_json = json.loads(forwarded_data) # merge in information from llvm backend @@ -402,12 +399,7 @@ def move_preasm(m): asm_setup = create_asm_setup(debug_tables, function_table_data, settings) basic_funcs = create_basic_funcs(function_table_sigs, settings) - basic_vars = create_basic_vars(forwarded_json, metadata, settings) - - # See if we need ASYNCIFY functions - # We might not need them even if ASYNCIFY is enabled - if need_asyncify(exported_implemented_functions): - basic_vars += ['___async', '___async_unwind', '___async_retval', '___async_cur_frame'] + basic_vars = create_basic_vars(exported_implemented_functions, forwarded_json, metadata, settings) function_tables_impls = make_function_tables_impls(function_table_sigs, settings) asm_setup += setup_function_pointers(function_table_sigs, settings) @@ -441,8 +433,8 @@ def check(extern): bg_funcs = basic_funcs + global_funcs bg_vars = basic_vars + global_vars - asm_global_funcs, asm_global_vars = create_asm_globals( - bg_funcs, bg_vars, access_quote, metadata, settings) + asm_global_funcs= create_asm_global_funcs(bg_funcs, metadata, settings) + asm_global_vars = create_asm_global_vars(bg_vars, settings) the_global = create_the_global(metadata, settings) sending_vars = basic_funcs + global_funcs + basic_vars + global_vars @@ -883,7 +875,8 @@ def closure(other): return closure -def create_asm_globals(bg_funcs, bg_vars, access_quote, metadata, settings): +def create_asm_global_funcs(bg_funcs, metadata, settings): + access_quote = access_quoter(settings) maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul', 'min', 'max', 'clz32']] if provide_fround(settings): maths += ['Math.fround'] @@ -893,12 +886,17 @@ def create_asm_globals(bg_funcs, bg_vars, access_quote, metadata, settings): asm_global_funcs += global_simd_funcs(access_quote, metadata, settings) if settings['USE_PTHREADS']: asm_global_funcs += ''.join([' var Atomics_' + ty + '=global' + access_quote('Atomics') + access_quote(ty) + ';\n' for ty in ['load', 'store', 'exchange', 'compareExchange', 'add', 'sub', 'and', 'or', 'xor']]) + return asm_global_funcs + +def create_asm_global_vars(bg_vars, settings): + access_quote = access_quoter(settings) asm_global_vars = ''.join([' var ' + g + '=env' + access_quote(g) + '|0;\n' for g in bg_vars]) if settings['BINARYEN'] and settings['SIDE_MODULE']: - asm_global_vars += '\n var STACKTOP = 0, STACK_MAX = 0;\n' # wasm side modules internally define their stack, these are set at module startup time + # wasm side modules internally define their stack, these are set at module startup time + asm_global_vars += '\n var STACKTOP = 0, STACK_MAX = 0;\n' - return asm_global_funcs, asm_global_vars + return asm_global_vars def global_simd_funcs(access_quote, metadata, settings): @@ -1076,7 +1074,7 @@ def create_basic_funcs(function_table_sigs, settings): basic_funcs += ['setTempRet0', 'getTempRet0'] return basic_funcs -def create_basic_vars(forwarded_json, metadata, settings): +def create_basic_vars(exported_implemented_functions, forwarded_json, metadata, settings): basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] if not (settings['BINARYEN'] and settings['SIDE_MODULE']): basic_vars += ['STACKTOP', 'STACK_MAX'] @@ -1090,6 +1088,11 @@ def create_basic_vars(forwarded_json, metadata, settings): basic_vars += ['gb', 'fb'] else: basic_vars += ['memoryBase', 'tableBase'] # wasm side modules have a specific convention for these + + # See if we need ASYNCIFY functions + # We might not need them even if ASYNCIFY is enabled + if need_asyncify(exported_implemented_functions): + basic_vars += ['___async', '___async_unwind', '___async_retval', '___async_cur_frame'] return basic_vars def create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings): From 774d51a3847f13a7f13f540b7ddca7678be16e8b Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 10:51:13 -0700 Subject: [PATCH 42/52] Fixup spacing --- emscripten.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/emscripten.py b/emscripten.py index 2141380ec8f4f..de846c055b8b4 100755 --- a/emscripten.py +++ b/emscripten.py @@ -97,14 +97,14 @@ def emscript(infile, settings, outfile, libraries=None, compiler_engine=None, glue, forwarded_data = compiler_glue(metadata, settings, libraries, compiler_engine, temp_files, DEBUG) with ToolchainProfiler.profile_block('function_tables_and_exports'): - (post, funcs_js, sending, receiving, asm_setup, the_global, - asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, + (post, funcs_js, sending, receiving, asm_setup, the_global, asm_global_vars, + asm_global_funcs, pre_tables, final_function_tables, exports, function_table_data, forwarded_json) = function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, settings, outfile, DEBUG) with ToolchainProfiler.profile_block('finalize_output'): - finalize_output(metadata, post, funcs_js, sending, - receiving, asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, - final_function_tables, exports, function_table_data, forwarded_json, settings, outfile, DEBUG) + finalize_output(metadata, post, funcs_js, sending, receiving, asm_setup, the_global, + asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, + exports, function_table_data, forwarded_json, settings, outfile, DEBUG) success = True @@ -1187,9 +1187,9 @@ def create_receiving(function_table_data, function_tables_defs, exported_impleme return receiving -def finalize_output(metadata, post, funcs_js, sending, receiving, - asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, - function_table_data, forwarded_json, settings, outfile, DEBUG): +def finalize_output(metadata, post, funcs_js, sending, receiving, asm_setup, the_global, + asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, exports, + function_table_data, forwarded_json, settings, outfile, DEBUG): if DEBUG: logging.debug('emscript: python processing: finalize') t = time.time() From f912d332559010f0b5be93b19f5a0a2096c4704f Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 10:52:20 -0700 Subject: [PATCH 43/52] Further consolidate asm_setup creation --- emscripten.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/emscripten.py b/emscripten.py index de846c055b8b4..06162c94a60af 100755 --- a/emscripten.py +++ b/emscripten.py @@ -397,7 +397,7 @@ def move_preasm(m): exported_implemented_functions = get_exported_implemented_functions( all_exported_functions, all_implemented, metadata, settings) - asm_setup = create_asm_setup(debug_tables, function_table_data, settings) + asm_setup = create_asm_setup(debug_tables, function_table_data, metadata, settings) basic_funcs = create_basic_funcs(function_table_sigs, settings) basic_vars = create_basic_vars(exported_implemented_functions, forwarded_json, metadata, settings) @@ -421,15 +421,6 @@ def move_preasm(m): .difference(set(global_vars)).difference(implemented_functions)) if settings['RELOCATABLE']: global_funcs += ['g$' + extern for extern in metadata['externs']] - side = 'parent' if settings['SIDE_MODULE'] else '' - def check(extern): - if settings['ASSERTIONS']: - return ('assert(' + side + 'Module["' + extern + '"], "external function \'' + extern + - '\' is missing. perhaps a side module was not linked in? if this symbol was expected to arrive ' - 'from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment");') - return '' - for extern in metadata['externs']: - asm_setup += 'var g$' + extern + ' = function() { ' + check(extern) + ' return ' + side + 'Module["' + extern + '"] };\n' bg_funcs = basic_funcs + global_funcs bg_vars = basic_vars + global_vars @@ -1029,7 +1020,7 @@ def provide_fround(settings): return settings['PRECISE_F32'] or settings['SIMD'] -def create_asm_setup(debug_tables, function_table_data, settings): +def create_asm_setup(debug_tables, function_table_data, metadata, settings): asm_setup = '' if settings['ASSERTIONS'] >= 2: for sig in function_table_data: @@ -1038,10 +1029,6 @@ def create_asm_setup(debug_tables, function_table_data, settings): function_table_sigs = function_table_data.keys() for sig in function_table_sigs: asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_sigs, settings) + 'abort(x) }\n' - if settings['RELOCATABLE']: - asm_setup += 'var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;\n' - if not settings['SIDE_MODULE']: - asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n' if settings['BINARYEN']: def table_size(table): table_contents = table[table.index('[') + 1: table.index(']')] @@ -1053,6 +1040,19 @@ def table_size(table): asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size if not settings['EMULATED_FUNCTION_POINTERS']: asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size + if settings['RELOCATABLE']: + asm_setup += 'var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;\n' + if not settings['SIDE_MODULE']: + asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n' + side = 'parent' if settings['SIDE_MODULE'] else '' + def check(extern): + if settings['ASSERTIONS']: + return ('assert(' + side + 'Module["' + extern + '"], "external function \'' + extern + + '\' is missing. perhaps a side module was not linked in? if this symbol was expected to arrive ' + 'from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment");') + return '' + for extern in metadata['externs']: + asm_setup += 'var g$' + extern + ' = function() { ' + check(extern) + ' return ' + side + 'Module["' + extern + '"] };\n' return asm_setup From cc4f9f0ceb987dd08f60921f906e5de0b9bd6694 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 10:54:21 -0700 Subject: [PATCH 44/52] Deindent some functions --- emscripten.py | 101 ++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/emscripten.py b/emscripten.py index 06162c94a60af..aeb7251b38d6d 100755 --- a/emscripten.py +++ b/emscripten.py @@ -765,69 +765,66 @@ def make_function_tables_impls(function_table_sigs, settings): def setup_function_pointers(function_table_sigs, settings): - asm_setup = '' - for sig in function_table_sigs: - shared.Settings.copy(settings) - asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' - if settings.get('RESERVED_FUNCTION_POINTERS'): - asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' - if settings.get('EMULATED_FUNCTION_POINTERS'): - args = ['a%d' % i for i in range(len(sig)-1)] - full_args = ['x'] + args - table_access = 'FUNCTION_TABLE_' + sig - if settings['SIDE_MODULE']: - table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one - if settings['BINARYEN']: - # wasm uses a Table, which means we have function pointer emulation capabilities all the time, at no cost. just call the table - table_access = "Module['wasmTable']" - table_read = table_access + '.get(x)' - else: - table_read = table_access + '[x]' - prelude = ''' + asm_setup = '' + for sig in function_table_sigs: + asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' + if settings.get('RESERVED_FUNCTION_POINTERS'): + asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' + if settings.get('EMULATED_FUNCTION_POINTERS'): + args = ['a%d' % i for i in range(len(sig)-1)] + full_args = ['x'] + args + table_access = 'FUNCTION_TABLE_' + sig + if settings['SIDE_MODULE']: + table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one + if settings['BINARYEN']: + # wasm uses a Table, which means we have function pointer emulation capabilities all the time, at no cost. just call the table + table_access = "Module['wasmTable']" + table_read = table_access + '.get(x)' + else: + table_read = table_access + '[x]' + prelude = ''' if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_sigs, settings)) - asm_setup += ''' + asm_setup += ''' function ftCall_%s(%s) {%s return %s(%s); } ''' % (sig, ', '.join(full_args), prelude, table_read, ', '.join(args)) - return asm_setup + return asm_setup def setup_basic_funcs(function_table_sigs, settings): - basic_funcs = [] - for sig in function_table_sigs: - shared.Settings.copy(settings) - basic_funcs.append('invoke_%s' % sig) - if settings.get('RESERVED_FUNCTION_POINTERS'): - basic_funcs.append('jsCall_%s' % sig) - if settings.get('EMULATED_FUNCTION_POINTERS'): - if not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls - basic_funcs.append('ftCall_%s' % sig) - return basic_funcs + basic_funcs = [] + for sig in function_table_sigs: + basic_funcs.append('invoke_%s' % sig) + if settings.get('RESERVED_FUNCTION_POINTERS'): + basic_funcs.append('jsCall_%s' % sig) + if settings.get('EMULATED_FUNCTION_POINTERS'): + if not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls + basic_funcs.append('ftCall_%s' % sig) + return basic_funcs def setup_funcs_js(function_table_sigs, settings): - funcs_js = [] - for sig in function_table_sigs: - shared.Settings.copy(settings) - if settings.get('EMULATED_FUNCTION_POINTERS'): - if settings.get('RELOCATABLE') and not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls - params = ','.join(['ptr'] + ['p%d' % p for p in range(len(sig)-1)]) - coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i', settings)] + [shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) - coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, unfloat(sig[p+1]), settings)) for p in range(len(sig)-1)]) + ';' - mini_coerced_params = ','.join([shared.JS.make_coercion('p%d', sig[p+1], settings) % p for p in range(len(sig)-1)]) - maybe_return = '' if sig[0] == 'v' else 'return' - final_return = maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', unfloat(sig[0]), settings) + ';' - if settings['EMULATED_FUNCTION_POINTERS'] == 1: - body = final_return - else: - body = ('if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + - shared.JS.make_coercion( - 'FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + - mini_coerced_params + ')', sig[0], settings, ffi_arg=True - ) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + final_return) - funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n') - return funcs_js + funcs_js = [] + for sig in function_table_sigs: + if settings.get('EMULATED_FUNCTION_POINTERS'): + if settings.get('RELOCATABLE') and not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls + params = ','.join(['ptr'] + ['p%d' % p for p in range(len(sig)-1)]) + coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i', settings)] + [shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) + coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, unfloat(sig[p+1]), settings)) for p in range(len(sig)-1)]) + ';' + mini_coerced_params = ','.join([shared.JS.make_coercion('p%d', sig[p+1], settings) % p for p in range(len(sig)-1)]) + maybe_return = '' if sig[0] == 'v' else 'return' + final_return = maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', unfloat(sig[0]), settings) + ';' + if settings['EMULATED_FUNCTION_POINTERS'] == 1: + body = final_return + else: + body = ('if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + + shared.JS.make_coercion( + 'FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + + mini_coerced_params + ')', sig[0], settings, ffi_arg=True + ) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + final_return) + funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n') + return funcs_js def get_function_pointer_error(sig, function_table_sigs, settings): From bd33d7cfc6597c2063786aa505746a50f3943f7c Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 10:55:55 -0700 Subject: [PATCH 45/52] Move function_tables_impls, also re-copy settings at least once --- emscripten.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/emscripten.py b/emscripten.py index aeb7251b38d6d..4aa87d8593a56 100755 --- a/emscripten.py +++ b/emscripten.py @@ -401,7 +401,8 @@ def move_preasm(m): basic_funcs = create_basic_funcs(function_table_sigs, settings) basic_vars = create_basic_vars(exported_implemented_functions, forwarded_json, metadata, settings) - function_tables_impls = make_function_tables_impls(function_table_sigs, settings) + shared.Settings.copy(settings) + asm_setup += setup_function_pointers(function_table_sigs, settings) basic_funcs += setup_basic_funcs(function_table_sigs, settings) funcs_js += setup_funcs_js(function_table_sigs, settings) @@ -434,6 +435,7 @@ def move_preasm(m): receiving = create_receiving(function_table_data, function_tables_defs, exported_implemented_functions, settings) + function_tables_impls = make_function_tables_impls(function_table_sigs, settings) final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs if settings.get('EMULATED_FUNCTION_POINTERS'): asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' From b5bcd5393d81682e9baf1d2de7e18504888877bc Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 11:00:13 -0700 Subject: [PATCH 46/52] Unindent make_function_tables_impls --- emscripten.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/emscripten.py b/emscripten.py index 4aa87d8593a56..bd4dfe01743c5 100755 --- a/emscripten.py +++ b/emscripten.py @@ -734,36 +734,36 @@ def math_fix(g): def make_function_tables_impls(function_table_sigs, settings): - function_tables_impls = [] - for sig in function_table_sigs: - args = ','.join(['a' + str(i) for i in range(1, len(sig))]) - arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) - coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) - ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings) - if not settings['EMULATED_FUNCTION_POINTERS']: - function_tables_impls.append(''' + function_tables_impls = [] + for sig in function_table_sigs: + args = ','.join(['a' + str(i) for i in range(1, len(sig))]) + arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) + coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) + ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings) + if not settings['EMULATED_FUNCTION_POINTERS']: + function_tables_impls.append(''' function dynCall_%s(index%s%s) { index = index|0; %s %s; } ''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret)) - else: - function_tables_impls.append(''' + else: + function_tables_impls.append(''' var dynCall_%s = ftCall_%s; ''' % (sig, sig)) - ffi_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings, ffi_arg=True) for i in range(1, len(sig))]) - for i in range(settings['RESERVED_FUNCTION_POINTERS']): - jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall_%s(%d%s%s)' % (sig, i, ',' if ffi_args else '', ffi_args), sig[0], settings, ffi_result=True) - function_tables_impls.append(''' + ffi_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings, ffi_arg=True) for i in range(1, len(sig))]) + for i in range(settings['RESERVED_FUNCTION_POINTERS']): + jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall_%s(%d%s%s)' % (sig, i, ',' if ffi_args else '', ffi_args), sig[0], settings, ffi_result=True) + function_tables_impls.append(''' function jsCall_%s_%s(%s) { %s %s; } ''' % (sig, i, args, arg_coercions, jsret)) - return function_tables_impls + return function_tables_impls def setup_function_pointers(function_table_sigs, settings): From 90dd5b0f72a69015b9e46731249c0ef8cf729af4 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 11:09:01 -0700 Subject: [PATCH 47/52] Contain all asm_setup creation into create_asm_setup --- emscripten.py | 76 +++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/emscripten.py b/emscripten.py index bd4dfe01743c5..39a8c8c5cdec8 100755 --- a/emscripten.py +++ b/emscripten.py @@ -403,7 +403,6 @@ def move_preasm(m): shared.Settings.copy(settings) - asm_setup += setup_function_pointers(function_table_sigs, settings) basic_funcs += setup_basic_funcs(function_table_sigs, settings) funcs_js += setup_funcs_js(function_table_sigs, settings) @@ -438,8 +437,13 @@ def move_preasm(m): function_tables_impls = make_function_tables_impls(function_table_sigs, settings) final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs if settings.get('EMULATED_FUNCTION_POINTERS'): - asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' - final_function_tables = final_function_tables.replace("asm['", '').replace("']", '').replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_').replace('var dynCall_', '//') + final_function_tables = ( + final_function_tables + .replace("asm['", '') + .replace("']", '') + .replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_') + .replace('var dynCall_', '//') + ) if DEBUG: logging.debug('asm text sizes' + str([ @@ -766,34 +770,6 @@ def make_function_tables_impls(function_table_sigs, settings): return function_tables_impls -def setup_function_pointers(function_table_sigs, settings): - asm_setup = '' - for sig in function_table_sigs: - asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' - if settings.get('RESERVED_FUNCTION_POINTERS'): - asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' - if settings.get('EMULATED_FUNCTION_POINTERS'): - args = ['a%d' % i for i in range(len(sig)-1)] - full_args = ['x'] + args - table_access = 'FUNCTION_TABLE_' + sig - if settings['SIDE_MODULE']: - table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one - if settings['BINARYEN']: - # wasm uses a Table, which means we have function pointer emulation capabilities all the time, at no cost. just call the table - table_access = "Module['wasmTable']" - table_read = table_access + '.get(x)' - else: - table_read = table_access + '[x]' - prelude = ''' - if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_sigs, settings)) - asm_setup += ''' -function ftCall_%s(%s) {%s - return %s(%s); -} -''' % (sig, ', '.join(full_args), prelude, table_read, ', '.join(args)) - return asm_setup - - def setup_basic_funcs(function_table_sigs, settings): basic_funcs = [] for sig in function_table_sigs: @@ -1020,12 +996,13 @@ def provide_fround(settings): def create_asm_setup(debug_tables, function_table_data, metadata, settings): + function_table_sigs = function_table_data.keys() + asm_setup = '' if settings['ASSERTIONS'] >= 2: for sig in function_table_data: asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';' if settings['ASSERTIONS']: - function_table_sigs = function_table_data.keys() for sig in function_table_sigs: asm_setup += '\nfunction nullFunc_' + sig + '(x) { ' + get_function_pointer_error(sig, function_table_sigs, settings) + 'abort(x) }\n' if settings['BINARYEN']: @@ -1052,6 +1029,41 @@ def check(extern): return '' for extern in metadata['externs']: asm_setup += 'var g$' + extern + ' = function() { ' + check(extern) + ' return ' + side + 'Module["' + extern + '"] };\n' + + asm_setup += setup_function_pointers(function_table_sigs, settings) + + if settings.get('EMULATED_FUNCTION_POINTERS'): + function_tables_impls = make_function_tables_impls(function_table_sigs, settings) + asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' + + return asm_setup + + +def setup_function_pointers(function_table_sigs, settings): + asm_setup = '' + for sig in function_table_sigs: + asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' + if settings.get('RESERVED_FUNCTION_POINTERS'): + asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' + if settings.get('EMULATED_FUNCTION_POINTERS'): + args = ['a%d' % i for i in range(len(sig)-1)] + full_args = ['x'] + args + table_access = 'FUNCTION_TABLE_' + sig + if settings['SIDE_MODULE']: + table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one + if settings['BINARYEN']: + # wasm uses a Table, which means we have function pointer emulation capabilities all the time, at no cost. just call the table + table_access = "Module['wasmTable']" + table_read = table_access + '.get(x)' + else: + table_read = table_access + '[x]' + prelude = ''' + if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_sigs, settings)) + asm_setup += ''' +function ftCall_%s(%s) {%s + return %s(%s); +} +''' % (sig, ', '.join(full_args), prelude, table_read, ', '.join(args)) return asm_setup From f38ca460415354f655cbb93f147fb587001cb60b Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 11:14:16 -0700 Subject: [PATCH 48/52] Consolidate basic_funcs creation --- emscripten.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/emscripten.py b/emscripten.py index 39a8c8c5cdec8..9b534c3591ce7 100755 --- a/emscripten.py +++ b/emscripten.py @@ -403,7 +403,6 @@ def move_preasm(m): shared.Settings.copy(settings) - basic_funcs += setup_basic_funcs(function_table_sigs, settings) funcs_js += setup_funcs_js(function_table_sigs, settings) exports = create_exports(exported_implemented_functions, in_table, function_table_data, metadata, settings) @@ -770,18 +769,6 @@ def make_function_tables_impls(function_table_sigs, settings): return function_tables_impls -def setup_basic_funcs(function_table_sigs, settings): - basic_funcs = [] - for sig in function_table_sigs: - basic_funcs.append('invoke_%s' % sig) - if settings.get('RESERVED_FUNCTION_POINTERS'): - basic_funcs.append('jsCall_%s' % sig) - if settings.get('EMULATED_FUNCTION_POINTERS'): - if not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls - basic_funcs.append('ftCall_%s' % sig) - return basic_funcs - - def setup_funcs_js(function_table_sigs, settings): funcs_js = [] for sig in function_table_sigs: @@ -1083,6 +1070,14 @@ def create_basic_funcs(function_table_sigs, settings): basic_funcs += ['nullFunc_' + sig] if settings['RELOCATABLE']: basic_funcs += ['setTempRet0', 'getTempRet0'] + + for sig in function_table_sigs: + basic_funcs.append('invoke_%s' % sig) + if settings.get('RESERVED_FUNCTION_POINTERS'): + basic_funcs.append('jsCall_%s' % sig) + if settings.get('EMULATED_FUNCTION_POINTERS'): + if not settings['BINARYEN']: # in wasm, emulated function pointers are just simple table calls + basic_funcs.append('ftCall_%s' % sig) return basic_funcs def create_basic_vars(exported_implemented_functions, forwarded_json, metadata, settings): From dabac3828355ecc90127610225d6bb31a246dae7 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 11:27:20 -0700 Subject: [PATCH 49/52] Twiddle tuple spacing --- emscripten.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/emscripten.py b/emscripten.py index 9b534c3591ce7..69c368c2b3dcc 100755 --- a/emscripten.py +++ b/emscripten.py @@ -451,9 +451,9 @@ def move_preasm(m): len(exports), len(the_global), len(sending), len(receiving)])) logging.debug(' emscript: python processing: function tables and exports took %s seconds' % (time.time() - t)) - return (post, funcs_js, sending, receiving, - asm_setup, the_global, asm_global_vars, asm_global_funcs, pre_tables, final_function_tables, - exports, function_table_data, forwarded_json) + return (post, funcs_js, sending, receiving, asm_setup, the_global, asm_global_vars, + asm_global_funcs, pre_tables, final_function_tables, exports, + function_table_data, forwarded_json) def memory_and_global_initializers(pre, metadata, mem_init, settings): From 8a2321e6d1ab6f30f29e3f8bceefece6b7ca8f2a Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 11:31:54 -0700 Subject: [PATCH 50/52] Group getXX/setXX functions --- emscripten.py | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/emscripten.py b/emscripten.py index 69c368c2b3dcc..8745f5b0b9597 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1237,69 +1237,69 @@ def finalize_output(metadata, post, funcs_js, sending, receiving, asm_setup, the ptr = ptr | 0; return HEAP8s[ptr >> SPLIT_MEMORY_BITS][ptr & SPLIT_MEMORY_MASK] | 0; } -function get16(ptr) { - ptr = ptr | 0; - return HEAP16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; -} -function get32(ptr) { - ptr = ptr | 0; - return HEAP32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] | 0; -} -function getU8(ptr) { - ptr = ptr | 0; - return HEAPU8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] | 0; -} -function getU16(ptr) { - ptr = ptr | 0; - return HEAPU16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; -} -function getU32(ptr) { - ptr = ptr | 0; - return HEAPU32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] | 0; -} -function getF32(ptr) { - ptr = ptr | 0; - return +HEAPF32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2]; // TODO: fround when present -} -function getF64(ptr) { - ptr = ptr | 0; - return +HEAPF64s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 3]; -} function set8(ptr, value) { ptr = ptr | 0; value = value | 0; HEAP8s[ptr >> SPLIT_MEMORY_BITS][ptr & SPLIT_MEMORY_MASK] = value; } +function get16(ptr) { + ptr = ptr | 0; + return HEAP16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; +} function set16(ptr, value) { ptr = ptr | 0; value = value | 0; HEAP16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] = value; } +function get32(ptr) { + ptr = ptr | 0; + return HEAP32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] | 0; +} function set32(ptr, value) { ptr = ptr | 0; value = value | 0; HEAP32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; } +function getU8(ptr) { + ptr = ptr | 0; + return HEAPU8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] | 0; +} function setU8(ptr, value) { ptr = ptr | 0; value = value | 0; HEAPU8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] = value; } +function getU16(ptr) { + ptr = ptr | 0; + return HEAPU16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; +} function setU16(ptr, value) { ptr = ptr | 0; value = value | 0; HEAPU16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] = value; } +function getU32(ptr) { + ptr = ptr | 0; + return HEAPU32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] | 0; +} function setU32(ptr, value) { ptr = ptr | 0; value = value | 0; HEAPU32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; } +function getF32(ptr) { + ptr = ptr | 0; + return +HEAPF32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2]; // TODO: fround when present +} function setF32(ptr, value) { ptr = ptr | 0; value = +value; HEAPF32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; } +function getF64(ptr) { + ptr = ptr | 0; + return +HEAPF64s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 3]; +} function setF64(ptr, value) { ptr = ptr | 0; value = +value; From fc7c2f20326c00b3b37503b7b0096dbe46b140c4 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 12:33:17 -0700 Subject: [PATCH 51/52] Generate getXX/setXX functions programmatically --- emscripten.py | 105 ++++++++++++++++---------------------------------- 1 file changed, 33 insertions(+), 72 deletions(-) diff --git a/emscripten.py b/emscripten.py index 8745f5b0b9597..aa781c752c13a 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1232,80 +1232,41 @@ def finalize_output(metadata, post, funcs_js, sending, receiving, asm_setup, the first_in_asm = '' if settings['SPLIT_MEMORY']: if not settings['SAFE_SPLIT_MEMORY']: - first_in_asm += ''' -function get8(ptr) { + def make_get_set(name, coercer, shift): + access = 'HEAP{name}s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> {shift}]'.format(name=name, shift=shift) + format_data = { + 'name': name, + 'coerced_value': coercer('value'), + 'access': access, + 'coerced_access': coercer(access), + } + getter = ''' +function get{name}(ptr) {{ ptr = ptr | 0; - return HEAP8s[ptr >> SPLIT_MEMORY_BITS][ptr & SPLIT_MEMORY_MASK] | 0; -} -function set8(ptr, value) { - ptr = ptr | 0; - value = value | 0; - HEAP8s[ptr >> SPLIT_MEMORY_BITS][ptr & SPLIT_MEMORY_MASK] = value; -} -function get16(ptr) { - ptr = ptr | 0; - return HEAP16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; -} -function set16(ptr, value) { - ptr = ptr | 0; - value = value | 0; - HEAP16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] = value; -} -function get32(ptr) { - ptr = ptr | 0; - return HEAP32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] | 0; -} -function set32(ptr, value) { - ptr = ptr | 0; - value = value | 0; - HEAP32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; -} -function getU8(ptr) { - ptr = ptr | 0; - return HEAPU8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] | 0; -} -function setU8(ptr, value) { + return {coerced_access}; +}}'''.format(**format_data) + setter = ''' +function set{name}(ptr, value) {{ ptr = ptr | 0; - value = value | 0; - HEAPU8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] = value; -} -function getU16(ptr) { - ptr = ptr | 0; - return HEAPU16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; -} -function setU16(ptr, value) { - ptr = ptr | 0; - value = value | 0; - HEAPU16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] = value; -} -function getU32(ptr) { - ptr = ptr | 0; - return HEAPU32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] | 0; -} -function setU32(ptr, value) { - ptr = ptr | 0; - value = value | 0; - HEAPU32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; -} -function getF32(ptr) { - ptr = ptr | 0; - return +HEAPF32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2]; // TODO: fround when present -} -function setF32(ptr, value) { - ptr = ptr | 0; - value = +value; - HEAPF32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; -} -function getF64(ptr) { - ptr = ptr | 0; - return +HEAPF64s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 3]; -} -function setF64(ptr, value) { - ptr = ptr | 0; - value = +value; - HEAPF64s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 3] = value; -} -''' + value = {coerced_value}; + {access} = value; +}}'''.format(**format_data) + return getter + setter + def int_coerce(s): + return s + ' | 0' + def float_coerce(s): + return '+' + s + get_set_types = [ + ('8', int_coerce, 0), + ('16', int_coerce, 1), + ('32', int_coerce, 2), + ('U8', int_coerce, 0), + ('U16', int_coerce, 1), + ('U32', int_coerce, 2), + ('F32', float_coerce, 2), # TODO: fround when present + ('F64', float_coerce, 3), + ] + first_in_asm += ''.join([make_get_set(*args) for args in get_set_types]) + '\n' first_in_asm += 'buffer = new ArrayBuffer(32); // fake\n' runtime_funcs = [] From aab5995ce73ae255ea5a1e19faca16e30ddc7330 Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Fri, 14 Apr 2017 12:41:46 -0700 Subject: [PATCH 52/52] Combine getter and setter strings --- emscripten.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/emscripten.py b/emscripten.py index aa781c752c13a..1c5b293fc8c1b 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1240,18 +1240,16 @@ def make_get_set(name, coercer, shift): 'access': access, 'coerced_access': coercer(access), } - getter = ''' + return ''' function get{name}(ptr) {{ ptr = ptr | 0; return {coerced_access}; -}}'''.format(**format_data) - setter = ''' +}} function set{name}(ptr, value) {{ ptr = ptr | 0; value = {coerced_value}; {access} = value; }}'''.format(**format_data) - return getter + setter def int_coerce(s): return s + ' | 0' def float_coerce(s):