diff options
-rwxr-xr-x | emcc | 109 | ||||
-rw-r--r-- | tests/cases/issue_39.ll (renamed from tests/issue_39.ll) | 0 | ||||
-rw-r--r-- | tests/cases/issue_39.txt | 1 | ||||
-rw-r--r-- | tests/hello_world_error.c | 8 | ||||
-rw-r--r-- | tests/hello_world_error.cpp | 8 | ||||
-rw-r--r-- | tests/runner.py | 877 | ||||
-rw-r--r-- | tools/js-optimizer.js | 146 | ||||
-rw-r--r-- | tools/shared.py | 79 | ||||
-rw-r--r-- | tools/test-js-optimizer-output.js | 80 | ||||
-rw-r--r-- | tools/test-js-optimizer.js | 96 |
10 files changed, 915 insertions, 489 deletions
@@ -79,6 +79,11 @@ from tools import shared DEBUG = os.environ.get('EMCC_DEBUG') TEMP_DIR = os.environ.get('EMCC_TEMP_DIR') +LEAVE_INPUTS_RAW = os.environ.get('EMCC_LEAVE_INPUTS_RAW') # Do not compile .ll files into .bc, just compile them with emscripten directly + # Not recommended, this is mainly for the test runner, or if you have some other + # specific need. + # One major limitation with this mode is that dlmalloc will not be added in. + # LLVM optimizations will also not be done. if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv) @@ -338,18 +343,7 @@ try: final_suffix = target.split('.')[-1] # Apply optimization level settings - if opt_level >= 1: - shared.Settings.ASSERTIONS = 0 - shared.Settings.DISABLE_EXCEPTION_CATCHING = 1 - if opt_level >= 2: - shared.Settings.RELOOP = 1 - if opt_level >= 3: - shared.Settings.CORRECT_SIGNS = 0 - shared.Settings.CORRECT_OVERFLOWS = 0 - shared.Settings.CORRECT_ROUNDINGS = 0 - shared.Settings.I64_MODE = 0 - shared.Settings.DOUBLE_MODE = 0 - print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)' + shared.Settings.apply_opt_level(opt_level, noisy=True) # Apply -s settings in newargs here (after optimization levels, so they can override them) for change in settings_changes: @@ -358,11 +352,12 @@ try: ## Compile source code to bitcode - if DEBUG: print >> sys.stderr, 'emcc: compiling to bitcode (%s)' % str(sys.argv) + if DEBUG: print >> sys.stderr, 'emcc: compiling to bitcode' # First, generate LLVM bitcode. For each input file, we get base.o with bitcode for input_file in input_files: if input_file.endswith(SOURCE_SUFFIXES): + if DEBUG: print >> sys.stderr, 'emcc: compiling source file: ', input_file output_file = in_temp(unsuffixed_basename(input_file) + '.o') args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file] if DEBUG: print >> sys.stderr, "emcc running:", call, ' '.join(args) @@ -372,12 +367,15 @@ try: sys.exit(1) else: # bitcode if input_file.endswith(('.bc', '.o')): + if DEBUG: print >> sys.stderr, 'emcc: copying bitcode file: ', input_file shutil.copyfile(input_file, in_temp(unsuffixed_basename(input_file) + '.o')) else: #.ll - shared.Building.llvm_as(input_file, in_temp(unsuffixed_basename(input_file) + '.o')) + if not LEAVE_INPUTS_RAW: + if DEBUG: print >> sys.stderr, 'emcc: assembling assembly file: ', input_file + shared.Building.llvm_as(input_file, in_temp(unsuffixed_basename(input_file) + '.o')) # Optimize, if asked to - if llvm_opt_level > 0: + if llvm_opt_level > 0 and not LEAVE_INPUTS_RAW: if DEBUG: print >> sys.stderr, 'emcc: LLVM opts' for input_file in input_files: try: @@ -411,53 +409,70 @@ try: extra_files_to_link = [] - # Check if we need to include dlmalloc. Note that we assume a single symbol is enough to know if we have/do not have dlmalloc. If you - # include just a few symbols but want the rest, this will not work. - need_dlmalloc = False - has_dlmalloc = False - for input_file in input_files: - symbols = shared.Building.llvm_nm(in_temp(unsuffixed_basename(input_file) + '.o')) - for malloc_def in ['malloc', 'free', 'calloc', 'memalign', 'realloc', 'valloc', 'pvalloc', 'mallinfo', 'mallopt', 'malloc_trim', 'malloc_stats', 'malloc_usable_size', 'malloc_footprint', 'malloc_max_footprint', 'independent_calloc', 'independent_comalloc']: - if malloc_def in symbols.undefs: - need_dlmalloc = True - if malloc_def in symbols.defs: - has_dlmalloc = True - if need_dlmalloc and not has_dlmalloc: - # We need to build and link dlmalloc in - if DEBUG: print >> sys.stderr, 'emcc: including dlmalloc' - Popen([shared.EMCC, shared.path_from_root('src', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=PIPE, stderr=PIPE).communicate() - if llvm_opt_level > 0: - shared.Building.llvm_opt(in_temp('dlmalloc.o'), LLVM_INTERNAL_OPT_LEVEL, safe=llvm_opt_level < 2) - extra_files_to_link.append(in_temp('dlmalloc.o')) - - # dlmalloc needs some sign correction. # If we are in mode 0, switch to 2. We will add our lines - try: - if shared.Settings.CORRECT_SIGNS == 0: raise Exception('we need to change to 2') - except: # we fail if equal to 0 - so we need to switch to 2 - or if CORRECT_SIGNS is not even in Settings - shared.Settings.CORRECT_SIGNS = 2 - if shared.Settings.CORRECT_SIGNS == 2: - shared.Settings.CORRECT_SIGNS_LINES = [shared.path_from_root('src', 'dlmalloc.c') + ':' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]] - # If we are in mode 1, we are correcting everything anyhow. If we are in mode 3, we will be corrected - # so all is well anyhow too. + if not LEAVE_INPUTS_RAW: + # Check if we need to include dlmalloc. Note that we assume a single symbol is enough to know if we have/do not have dlmalloc. If you + # include just a few symbols but want the rest, this will not work. + need_dlmalloc = False + has_dlmalloc = False + for input_file in input_files: + symbols = shared.Building.llvm_nm(in_temp(unsuffixed_basename(input_file) + '.o')) + for malloc_def in ['malloc', 'free', 'calloc', 'memalign', 'realloc', 'valloc', 'pvalloc', 'mallinfo', 'mallopt', 'malloc_trim', 'malloc_stats', 'malloc_usable_size', 'malloc_footprint', 'malloc_max_footprint', 'independent_calloc', 'independent_comalloc']: + if malloc_def in symbols.undefs: + need_dlmalloc = True + if malloc_def in symbols.defs: + has_dlmalloc = True + if need_dlmalloc and not has_dlmalloc: + # We need to build and link dlmalloc in + if DEBUG: print >> sys.stderr, 'emcc: including dlmalloc' + Popen([shared.EMCC, shared.path_from_root('src', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=PIPE, stderr=PIPE).communicate() + if llvm_opt_level > 0: + shared.Building.llvm_opt(in_temp('dlmalloc.o'), LLVM_INTERNAL_OPT_LEVEL, safe=llvm_opt_level < 2) + extra_files_to_link.append(in_temp('dlmalloc.o')) + + # dlmalloc needs some sign correction. # If we are in mode 0, switch to 2. We will add our lines + try: + if shared.Settings.CORRECT_SIGNS == 0: raise Exception('we need to change to 2') + except: # we fail if equal to 0 - so we need to switch to 2 - or if CORRECT_SIGNS is not even in Settings + shared.Settings.CORRECT_SIGNS = 2 + if shared.Settings.CORRECT_SIGNS == 2: + shared.Settings.CORRECT_SIGNS_LINES = [shared.path_from_root('src', 'dlmalloc.c') + ':' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]] + # If we are in mode 1, we are correcting everything anyhow. If we are in mode 3, we will be corrected + # so all is well anyhow too. # First, combine the bitcode files if there are several if len(input_files) + len(extra_files_to_link) > 1: - shared.Building.link(map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + extra_files_to_link, + linker_inputs = map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + extra_files_to_link + if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs + shared.Building.link(linker_inputs, in_temp(target_basename + '.bc')) # TODO: LLVM link-time opts? here and/or elsewhere? else: - shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc')) + if not LEAVE_INPUTS_RAW: + shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc')) # Emscripten if opt_level >= 2: print >> sys.stderr, 'Warning: The relooper optimization can be very slow.' - final = shared.Building.emscripten(in_temp(target_basename + '.bc'), append_ext=False) + if not LEAVE_INPUTS_RAW: + emscripten_input = in_temp(target_basename + '.bc') + else: + assert len(input_files) == 1 + emscripten_input = input_files[0] + final = shared.Building.emscripten(emscripten_input, append_ext=False) + + # Apply a source code transformation, if requested + source_transform = os.environ.get('EMCC_JS_PROCESSOR') + if source_transform: + exec source_transform in locals() + shutil.copyfile(final, final + '.tr.js') + final += '.tr.js' + process(final) if opt_level >= 1: # js optimizer if DEBUG: print >> sys.stderr, 'emcc: running pre-closure post-opts' - final = shared.Building.js_optimizer(final, 'loopOptimizer') + final = shared.Building.js_optimizer(final, ['hoistMultiples', 'loopOptimizer']) # eliminator final = shared.Building.eliminator(final) diff --git a/tests/issue_39.ll b/tests/cases/issue_39.ll index 96460693..96460693 100644 --- a/tests/issue_39.ll +++ b/tests/cases/issue_39.ll diff --git a/tests/cases/issue_39.txt b/tests/cases/issue_39.txt new file mode 100644 index 00000000..3b85d897 --- /dev/null +++ b/tests/cases/issue_39.txt @@ -0,0 +1 @@ +*yes* diff --git a/tests/hello_world_error.c b/tests/hello_world_error.c new file mode 100644 index 00000000..c76b5baf --- /dev/null +++ b/tests/hello_world_error.c @@ -0,0 +1,8 @@ +#in clu de <st dio.h> + +int main() { + cheez burger + printf("hello, world!\n"); + return 1; +} + diff --git a/tests/hello_world_error.cpp b/tests/hello_world_error.cpp new file mode 100644 index 00000000..c76b5baf --- /dev/null +++ b/tests/hello_world_error.cpp @@ -0,0 +1,8 @@ +#in clu de <st dio.h> + +int main() { + cheez burger + printf("hello, world!\n"); + return 1; +} + diff --git a/tests/runner.py b/tests/runner.py index 49339f51..ae72ec49 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -21,6 +21,8 @@ def path_from_root(*pathelems): return os.path.join(__rootpath__, *pathelems) exec(open(path_from_root('tools', 'shared.py'), 'r').read()) +sys.path += [path_from_root('')] + # Sanity check for config try: @@ -90,8 +92,29 @@ class RunnerCore(unittest.TestCase): shutil.move(filename + '.o.ll', filename + '.o.ll.post') # for comparisons later Building.llvm_dis(filename) + # Generate JS from ll, and optionally modify the generated JS with a post_build function. Note + # that post_build is called on unoptimized JS, so we send it to emcc (otherwise, if run after + # emcc, it would not apply on the optimized/minified JS) + def ll_to_js(self, filename, extra_emscripten_args, post_build): + if self.emcc_args is None: + Building.emscripten(filename, append_ext=True, extra_args=extra_emscripten_args) + if post_build is not None: + exec post_build in locals() + shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js') + process(filename + '.o.js') + else: + if post_build is not None: + os.environ['EMCC_JS_PROCESSOR'] = post_build + else: + try: + del os.environ['EMCC_JS_PROCESSOR'] + except: + pass + Building.emcc(filename + '.o.ll', Settings.serialize() + self.emcc_args, filename + '.o.js') + # Build JavaScript code from source code - def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[]): + def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[], post_build=None): + # Copy over necessary files for compiling the source if main_file is None: f = open(filename, 'w') @@ -138,7 +161,11 @@ class RunnerCore(unittest.TestCase): # Finalize self.prep_ll_run(filename, filename + '.o', build_ll_hook=build_ll_hook) - Building.emscripten(filename, output_processor, extra_args=extra_emscripten_args) + # BC => JS + self.ll_to_js(filename, extra_emscripten_args, post_build) + + if output_processor is not None: + output_processor(open(filename + '.o.js').read()) def run_generated_code(self, engine, filename, args=[], check_timeout=True): stdout = os.path.join(self.get_dir(), 'stdout') # use files, as PIPE can get too full and hang us @@ -233,10 +260,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): filename = os.path.join(dirname, basename) if not no_build: self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes, - build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args) - - if post_build is not None: - post_build(filename + '.o.js') + build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args, post_build=post_build) # If not provided with expected output, then generate it right now, using lli if expected_output is None: @@ -261,18 +285,19 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): # No building - just process an existing .ll file (or .bc, which we turn into .ll) def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[]): - filename = os.path.join(self.get_dir(), 'src.cpp') self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook) - Building.emscripten(filename, extra_args=extra_emscripten_args) + + self.ll_to_js(filename, extra_emscripten_args, post_build) + self.do_run(None, expected_output, args, no_build=True, js_engines=js_engines, output_nicerizer=output_nicerizer, - post_build=post_build) + post_build=None) # post_build was already done in ll_to_js, this do_run call is just to test the output def test_hello_world(self): src = ''' @@ -782,10 +807,6 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): ''' self.do_run(src, '*yes*') - # Test for issue 39 - if not Building.LLVM_OPTS: - self.do_ll_run(path_from_root('tests', 'issue_39.ll'), '*yes*') - def test_if_else(self): src = ''' #include <stdio.h> @@ -1624,9 +1645,11 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): } ''' - def check(filename): - src = open(filename, 'r').read() - # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src + check = ''' +def process(filename): + src = open(filename, 'r').read() + # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src +''' self.do_run(src, 'hello world!\n*100*', post_build=check) @@ -2406,14 +2429,16 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): } ''' Settings.BUILD_AS_SHARED_LIB = 0 - def add_pre_run_and_checks(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);''' - ) - open(filename, 'w').write(src) + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' self.do_run(src, 'Constructing main object.\nConstructing lib object.\n', - post_build=add_pre_run_and_checks) + post_build=add_pre_run_and_checks) def test_dlfcn_qsort(self): if Settings.USE_TYPED_ARRAYS == 2: @@ -2493,15 +2518,17 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): ''' Settings.BUILD_AS_SHARED_LIB = 0 Settings.EXPORTED_FUNCTIONS = ['_main'] - def add_pre_run_and_checks(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);''' - ) - open(filename, 'w').write(src) + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *', - output_nicerizer=lambda x: x.replace('\n', '*'), - post_build=add_pre_run_and_checks) + output_nicerizer=lambda x: x.replace('\n', '*'), + post_build=add_pre_run_and_checks) def test_dlfcn_data_and_fptr(self): if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func') @@ -2591,12 +2618,14 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): Settings.BUILD_AS_SHARED_LIB = 0 Settings.EXPORTED_FUNCTIONS = ['_main'] Settings.EXPORTED_GLOBALS = [] - def add_pre_run_and_checks(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);''' - ) - open(filename, 'w').write(src) + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*', output_nicerizer=lambda x: x.replace('\n', '*'), post_build=add_pre_run_and_checks) @@ -2639,16 +2668,18 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): Settings.BUILD_AS_SHARED_LIB = 0 Settings.INCLUDE_FULL_LIBRARY = 1 Settings.EXPORTED_FUNCTIONS = ['_main'] - def add_pre_run_and_checks(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);''' - ) - open(filename, 'w').write(src) + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' self.do_run(src, 'Parent global: 123.*Parent global: 456.*', - output_nicerizer=lambda x: x.replace('\n', '*'), - post_build=add_pre_run_and_checks, - extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h']) + output_nicerizer=lambda x: x.replace('\n', '*'), + post_build=add_pre_run_and_checks, + extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h']) Settings.INCLUDE_FULL_LIBRARY = 0 def test_dlfcn_varargs(self): @@ -2697,14 +2728,16 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): ''' Settings.BUILD_AS_SHARED_LIB = 0 Settings.EXPORTED_FUNCTIONS = ['_main'] - def add_pre_run_and_checks(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);''' - ) - open(filename, 'w').write(src) + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' self.do_run(src, '100\n200\n13\n42\n', - post_build=add_pre_run_and_checks) + post_build=add_pre_run_and_checks) def test_rand(self): src = r''' @@ -2975,23 +3008,27 @@ at function.:blag self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h']) def test_files(self): - Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both. - def post(filename): - src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours - '// {{PRE_RUN_ADDITIONS}}', - ''' - FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory - FS.createLazyFile('/', 'test.file', 'test.file', true, false); - FS.root.write = true; - var test_files_input = 'hi there!'; - var test_files_input_index = 0; - FS.init(function() { - return test_files_input.charCodeAt(test_files_input_index++) || null; - }); - ''' - ) - open(filename, 'w').write(src) + if self.emcc_args is not None and '-O2' in self.emcc_args: + self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff + Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both. + post = ''' +def process(filename): + src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours + '// {{PRE_RUN_ADDITIONS}}', + \'\'\' + FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory + FS.createLazyFile('/', 'test.file', 'test.file', true, false); + FS.root.write = true; + var test_files_input = 'hi there!'; + var test_files_input_index = 0; + FS.init(function() { + return test_files_input.charCodeAt(test_files_input_index++) || null; + }); + \'\'\' + ) + open(filename, 'w').write(src) +''' other = open(os.path.join(self.get_dir(), 'test.file'), 'w') other.write('some data'); other.close() @@ -3001,20 +3038,21 @@ at function.:blag post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_folders(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - ''' - FS.createFolder('/', 'test', true, false); - FS.createPath('/', 'test/hello/world/', true, false); - FS.createPath('/test', 'goodbye/world/', true, false); - FS.createPath('/test/goodbye', 'noentry', false, false); - FS.createDataFile('/test', 'freeforall.ext', 'abc', true, true); - FS.createDataFile('/test', 'restricted.ext', 'def', false, false); - ''' - ) - open(filename, 'w').write(src) - + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + \'\'\' + FS.createFolder('/', 'test', true, false); + FS.createPath('/', 'test/hello/world/', true, false); + FS.createPath('/test', 'goodbye/world/', true, false); + FS.createPath('/test/goodbye', 'noentry', false, false); + FS.createDataFile('/test', 'freeforall.ext', 'abc', true, true); + FS.createDataFile('/test', 'restricted.ext', 'def', false, false); + \'\'\' + ) + open(filename, 'w').write(src) +''' src = r''' #include <stdio.h> #include <dirent.h> @@ -3079,69 +3117,79 @@ at function.:blag self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run) def test_stat(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - ''' - var f1 = FS.createFolder('/', 'test', true, true); - var f2 = FS.createDataFile(f1, 'file', 'abcdef', true, true); - var f3 = FS.createLink(f1, 'link', 'file', true, true); - var f4 = FS.createDevice(f1, 'device', function(){}, function(){}); - f1.timestamp = f2.timestamp = f3.timestamp = f4.timestamp = new Date(1200000000000); - ''' - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + \'\'\' + var f1 = FS.createFolder('/', 'test', true, true); + var f2 = FS.createDataFile(f1, 'file', 'abcdef', true, true); + var f3 = FS.createLink(f1, 'link', 'file', true, true); + var f4 = FS.createDevice(f1, 'device', function(){}, function(){}); + f1.timestamp = f2.timestamp = f3.timestamp = f4.timestamp = new Date(1200000000000); + \'\'\' + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'stat', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'stat', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_fcntl(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createDataFile('/', 'test', 'abcdef', true, true);" - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'test', 'abcdef', true, true);" + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'fcntl', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'fcntl', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_fcntl_open(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - ''' - FS.createDataFile('/', 'test-file', 'abcdef', true, true); - FS.createFolder('/', 'test-folder', true, true); - FS.root.write = true; - ''' - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + \'\'\' + FS.createDataFile('/', 'test-file', 'abcdef', true, true); + FS.createFolder('/', 'test-folder', true, true); + FS.root.write = true; + \'\'\' + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'fcntl-open', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'fcntl-open', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_fcntl_misc(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createDataFile('/', 'test', 'abcdef', true, true);" - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'test', 'abcdef', true, true);" + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'fcntl-misc', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'fcntl-misc', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_poll(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - ''' - FS.createDataFile('/', 'file', 'abcdef', true, true); - FS.createDevice('/', 'device', function() {}, function() {}); - ''' - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + \'\'\' + FS.createDataFile('/', 'file', 'abcdef', true, true); + FS.createDevice('/', 'device', function() {}, function() {}); + \'\'\' + ) + open(filename, 'w').write(src) +''' src = r''' #include <stdio.h> #include <errno.h> @@ -3283,22 +3331,23 @@ at function.:blag self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) def test_utime(self): - def add_pre_run_and_checks(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - ''' - var TEST_F1 = FS.createFolder('/', 'writeable', true, true); - var TEST_F2 = FS.createFolder('/', 'unwriteable', true, false); - ''' - ).replace( - '// {{POST_RUN_ADDITIONS}}', - ''' - print('first changed: ' + (TEST_F1.timestamp == 1200000000000)); - print('second changed: ' + (TEST_F2.timestamp == 1200000000000)); - ''' - ) - open(filename, 'w').write(src) - + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + \'\'\' + var TEST_F1 = FS.createFolder('/', 'writeable', true, true); + var TEST_F2 = FS.createFolder('/', 'unwriteable', true, false); + \'\'\' + ).replace( + '// {{POST_RUN_ADDITIONS}}', + \'\'\' + print('first changed: ' + (TEST_F1.timestamp == 1200000000000)); + print('second changed: ' + (TEST_F2.timestamp == 1200000000000)); + \'\'\' + ) + open(filename, 'w').write(src) +''' src = r''' #include <stdio.h> #include <errno.h> @@ -3329,11 +3378,14 @@ at function.:blag def test_fs_base(self): Settings.INCLUDE_FULL_LIBRARY = 1 try: - def addJS(filename): - src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'filesystem', 'src.js'), 'r').read()) - open(filename, 'w').write(src) + addJS = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'filesystem', 'src.js'), 'r').read()) + open(filename, 'w').write(src) +''' src = 'int main() {return 0;}\n' expected = open(path_from_root('tests', 'filesystem', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=addJS, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) @@ -3341,23 +3393,29 @@ at function.:blag Settings.INCLUDE_FULL_LIBRARY = 0 def test_unistd_access(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'access.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) def test_unistd_curdir(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'curdir.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) @@ -3373,12 +3431,15 @@ at function.:blag self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h']) def test_unistd_ttyname(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'ttyname.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'ttyname.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'ttyname.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) @@ -3394,12 +3455,15 @@ at function.:blag self.do_run(src, expected) def test_unistd_truncate(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'truncate.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) @@ -3410,12 +3474,15 @@ at function.:blag self.do_run(src, expected) def test_unistd_isatty(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'isatty.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'isatty.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'isatty.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) @@ -3431,23 +3498,29 @@ at function.:blag self.do_run(src, expected) def test_unistd_unlink(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'unlink.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'unlink.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'unlink.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) def test_unistd_links(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'links.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) @@ -3458,12 +3531,15 @@ at function.:blag self.do_run(src, expected) def test_unistd_io(self): - def add_pre_run(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(path_from_root('tests', 'unistd', 'io.js'), 'r').read() - ) - open(filename, 'w').write(src) + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read() self.do_run(src, expected, post_build=add_pre_run) @@ -3625,7 +3701,7 @@ at function.:blag self.do_run(src, '*1,0*', ['200', '1'], extra_emscripten_args=['-m']) self.do_run(src, '*400,0*', ['400', '400'], extra_emscripten_args=['-m'], no_build=True) - if self.use_defaults: # TODO: do this in other passes too, passing their opts into emcc + if self.emcc_args == []: # TODO: do this in other passes too, passing their opts into emcc # emcc should build in dlmalloc automatically, and do all the sign correction etc. for it try_delete(os.path.join(self.get_dir(), 'src.cpp.o.js')) @@ -3683,20 +3759,25 @@ at function.:blag # print opt, "FAIL" def test_lua(self): - if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') - - # Overflows in luaS_newlstr hash loop - Settings.SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type) - Settings.CORRECT_OVERFLOWS = 1 - Settings.CHECK_OVERFLOWS = 0 - Settings.CORRECT_SIGNS = 1 # Not sure why, but needed - Settings.INIT_STACK = 1 # TODO: Investigate why this is necessary - - self.do_ll_run(path_from_root('tests', 'lua', 'lua.ll'), - 'hello lua world!\n17\n1\n2\n3\n4\n7', - args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''], - output_nicerizer=lambda string: string.replace('\n\n', '\n').replace('\n\n', '\n'), - extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) + try: + os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' + + if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') + + # Overflows in luaS_newlstr hash loop + Settings.SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type) + Settings.CORRECT_OVERFLOWS = 1 + Settings.CHECK_OVERFLOWS = 0 + Settings.CORRECT_SIGNS = 1 # Not sure why, but needed + Settings.INIT_STACK = 1 # TODO: Investigate why this is necessary + + self.do_ll_run(path_from_root('tests', 'lua', 'lua.ll'), + 'hello lua world!\n17\n1\n2\n3\n4\n7', + args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''], + output_nicerizer=lambda string: string.replace('\n\n', '\n').replace('\n\n', '\n'), + extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) + finally: + del os.environ['EMCC_LEAVE_INPUTS_RAW'] def get_build_dir(self): return os.path.join(self.get_dir(), 'building') @@ -3713,16 +3794,18 @@ at function.:blag if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed - def post(filename): - # Embed the font into the document - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - '''FS.createDataFile('/', 'font.ttf', %s, true, false);''' % str( - map(ord, open(path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read()) - ) - ) - open(filename, 'w').write(src) - + post = ''' +def process(filename): + import tools.shared as shared + # Embed the font into the document + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'font.ttf', %s, true, false);" % str( + map(ord, open(shared.path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read()) + ) + ) + open(filename, 'w').write(src) +''' # Main self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(), open(path_from_root('tests', 'freetype', 'ref.txt'), 'r').read(), @@ -3739,7 +3822,7 @@ at function.:blag pgo_data = read_pgo_data(path_from_root('tests', 'sqlite', 'sqlite-autooptimize.fails.txt')) - Settings.CORRECT_SIGNS = 2 + Settings.CORRECT_SIGNS = 1 # XXX: in default, we fail with 2 here, even though the pgo_data should be correct (and works in s_0_0). Investigate this. Settings.CORRECT_SIGNS_LINES = pgo_data['signs_lines'] Settings.CORRECT_OVERFLOWS = 0 Settings.CORRECT_ROUNDINGS = 0 @@ -3750,18 +3833,20 @@ at function.:blag Settings.INVOKE_RUN = 0 # We append code that does run() ourselves - def post(filename): - src = open(filename, 'a') - src.write(''' - FS.init(); - FS.root.write = true; - FS.ignorePermissions = true; // /dev is read-only - FS.createPath('/', 'dev/shm/tmp', true, true); - FS.ignorePermissions = false; - FS.currentPath = '/dev/shm/tmp'; - run(); - ''') - src.close() + post = ''' +def process(filename): + src = open(filename, 'a') + src.write(\'\'\' + FS.init(); + FS.root.write = true; + FS.ignorePermissions = true; // /dev is read-only + FS.createPath('/', 'dev/shm/tmp', true, true); + FS.ignorePermissions = false; + FS.currentPath = '/dev/shm/tmp'; + run(); + \'\'\') + src.close() +''' self.do_run(r''' #define SQLITE_DISABLE_LFS @@ -3807,7 +3892,7 @@ at function.:blag js_engines=[SPIDERMONKEY_ENGINE]) # V8 issue 1407 def test_poppler(self): - if not self.use_defaults: return self.skip('very slow, we only do this in default') + if not self.emcc_args == []: return self.skip('very slow, we only do this in default') Settings.SAFE_HEAP = 0 # Has variable object @@ -3827,20 +3912,22 @@ at function.:blag input_file.write(str(map(ord, open(path_from_root('tests', 'poppler', 'paper.pdf'), 'rb').read()))) input_file.close() - def post(filename): - # To avoid loading this large file to memory and altering it, we simply append to the end - src = open(filename, 'a') - src.write( - ''' - FS.ignorePermissions = true; - FS.createDataFile('/', 'paper.pdf', eval(read('paper.pdf.js')), true, false); - FS.root.write = true; - FS.ignorePermissions = false; - run(); - print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) }))); - ''' - ) - src.close() + post = ''' +def process(filename): + # To avoid loading this large file to memory and altering it, we simply append to the end + src = open(filename, 'a') + src.write( + \'\'\' + FS.ignorePermissions = true; + FS.createDataFile('/', 'paper.pdf', eval(read('paper.pdf.js')), true, false); + FS.root.write = true; + FS.ignorePermissions = false; + run(); + print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) }))); + \'\'\' + ) + src.close() +''' #fontconfig = self.get_library('fontconfig', [os.path.join('src', '.libs', 'libfontconfig.a')]) # Used in file, but not needed, mostly @@ -3873,19 +3960,21 @@ at function.:blag Settings.CORRECT_SIGNS = 2 Settings.CORRECT_SIGNS_LINES = ["mqc.c:566", "mqc.c:317"] - original_j2k = path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k') - - def post(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - '''FS.createDataFile('/', 'image.j2k', %s, true, false);FS.root.write = true;''' % line_splitter(str( - map(ord, open(original_j2k, 'rb').read()) - )) - ).replace( - '// {{POST_RUN_ADDITIONS}}', - '''print("Data: " + JSON.stringify(FS.root.contents['image.raw'].contents));''' - ) - open(filename, 'w').write(src) + post = ''' +def process(filename): + import tools.shared as shared + original_j2k = shared.path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k') + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'image.j2k', %s, true, false);FS.root.write = true;" % shared.line_splitter(str( + map(ord, open(original_j2k, 'rb').read()) + )) + ).replace( + '// {{POST_RUN_ADDITIONS}}', + "print('Data: ' + JSON.stringify(FS.root.contents['image.raw'].contents));" + ) + open(filename, 'w').write(src) +''' shutil.copy(path_from_root('tests', 'openjpeg', 'opj_config.h'), self.get_dir()) @@ -3967,31 +4056,37 @@ at function.:blag # They are only valid enough for us to read for test purposes, not for llvm-as # to process. def test_cases(self): - self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed + try: + self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed - Settings.CHECK_OVERFLOWS = 0 - if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") + os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' + + Settings.CHECK_OVERFLOWS = 0 + if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") + + for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): + shortname = name.replace('.ll', '') + if '' not in shortname: continue + print "Testing case '%s'..." % shortname + output_file = path_from_root('tests', 'cases', shortname + '.txt') + if Settings.QUANTUM_SIZE == 1: + q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt') + if os.path.exists(q1_output_file): + output_file = q1_output_file + if os.path.exists(output_file): + output = open(output_file, 'r').read() + else: + output = 'hello, world!' + if output.rstrip() != 'skip': + self.do_ll_run(path_from_root('tests', 'cases', name), output) + # Optional source checking, a python script that gets a global generated with the source + src_checker = path_from_root('tests', 'cases', shortname + '.py') + if os.path.exists(src_checker): + generated = open('src.cpp.o.js').read() + exec(open(src_checker).read()) - for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): - shortname = name.replace('.ll', '') - if '' not in shortname: continue - print "Testing case '%s'..." % shortname - output_file = path_from_root('tests', 'cases', shortname + '.txt') - if Settings.QUANTUM_SIZE == 1: - q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt') - if os.path.exists(q1_output_file): - output_file = q1_output_file - if os.path.exists(output_file): - output = open(output_file, 'r').read() - else: - output = 'hello, world!' - if output.rstrip() != 'skip': - self.do_ll_run(path_from_root('tests', 'cases', name), output) - # Optional source checking, a python script that gets a global generated with the source - src_checker = path_from_root('tests', 'cases', shortname + '.py') - if os.path.exists(src_checker): - generated = open('src.cpp.o.js').read() - exec(open(src_checker).read()) + finally: + del os.environ['EMCC_LEAVE_INPUTS_RAW'] # Autodebug the code def do_autodebug(self, filename): @@ -4087,12 +4182,14 @@ at function.:blag } ''' - def post1(filename): - src = open(filename, 'a') - src.write(''' - Profiling.dump(); - ''') - src.close() + post1 = ''' +def process(filename): + src = open(filename, 'a') + src.write(\'\'\' + Profiling.dump(); + \'\'\') + src.close() +''' self.do_run(src, '''Profiling data: Block 0: ''', post_build=post1) @@ -4134,16 +4231,18 @@ Block 0: ''', post_build=post1) } ''' - def post(filename): - src = open(filename, 'a') - src.write(''' - startProfiling(); - run(); - stopProfiling(); - printProfiling(); - print('*ok*'); - ''') - src.close() + post = ''' +def process(filename): + src = open(filename, 'a') + src.write(\'\'\' + startProfiling(); + run(); + stopProfiling(); + printProfiling(); + print('*ok*'); + \'\'\') + src.close() +''' self.do_run(src, ': __Z6inner1i (5000)\n', post_build=post) @@ -4182,15 +4281,17 @@ Block 0: ''', post_build=post1) _free(sme); print('*ok*'); ''' - def post(filename): - Popen(['python', DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate() - Popen(['python', NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate() - src = open(filename, 'r').read().replace( - '// {{MODULE_ADDITIONS}', - 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' + - '// {{MODULE_ADDITIONS}' - ) - open(filename, 'w').write(src) + post = ''' +def process(filename): + Popen(['python', DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate() + Popen(['python', NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate() + src = open(filename, 'r').read().replace( + '// {{MODULE_ADDITIONS}', + 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' + + '// {{MODULE_ADDITIONS}' + ) + open(filename, 'w').write(src) +''' # XXX disable due to possible v8 bug -- self.do_run(src, '*166*\n*ok*', post_build=post) # Way 2: use CppHeaderParser @@ -4247,7 +4348,9 @@ Block 0: ''', post_build=post1) #include "bindingtest.cpp" ''' - script_src_2 = ''' + post2 = ''' +def process(filename): + script_src_2 = \'\'\' var sme = new Parent(42); sme.mulVal(2); print('*') @@ -4329,15 +4432,15 @@ Block 0: ''', post_build=post1) c2.virtualFunc2(); print('*ok*'); - ''' + \'\'\' + src = open(filename, 'r').read().replace( + '// {{MODULE_ADDITIONS}', + open('bindingtest.js').read() + '\\n\\n' + script_src_2 + '\\n\\n' + + '// {{MODULE_ADDITIONS}' + ) + open(filename, 'w').write(src) +''' - def post2(filename): - src = open(filename, 'r').read().replace( - '// {{MODULE_ADDITIONS}', - open(os.path.join(self.get_dir(), 'bindingtest.js')).read() + '\n\n' + script_src_2 + '\n\n' + - '// {{MODULE_ADDITIONS}' - ) - open(filename, 'w').write(src) self.do_run(src, '''* 84 c1 @@ -4404,21 +4507,24 @@ Child2:9 } ''' - def post(filename): - src = open(filename, 'r').read().replace( - '// {{POST_RUN_ADDITIONS}}', - ''' - if (Runtime.typeInfo) { - print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|'); - var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser') - print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|'); - print('|' + JSON.stringify(Runtime.generateStructInfo(null, 'UserStruct')) + '|'); - } else { - print('No type info.'); - } - ''' - ) - open(filename, 'w').write(src) + post = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{POST_RUN_ADDITIONS}}', + \'\'\' + if (Runtime.typeInfo) { + print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|'); + var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser') + print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|'); + print('|' + JSON.stringify(Runtime.generateStructInfo(null, 'UserStruct')) + '|'); + } else { + print('No type info.'); + } + \'\'\' + ) + open(filename, 'w').write(src) +''' + self.do_run(src, '*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|\n|{"__size__":8,"x":0,"y":4,"z":6}|', post_build=post) @@ -4429,47 +4535,6 @@ Child2:9 ### Tests for tools - def test_closure_compiler(self): - src = ''' - #include<stdio.h> - int main() { - printf("*closured*\\n"); - - FILE *file = fopen("somefile.binary", "rb"); - char buffer[1024]; - size_t read = fread(buffer, 1, 4, file); - printf("data: %d", buffer[0]); - for (int i = 1; i < 4; i++) - printf(",%d", buffer[i]); - printf("\\n"); - - return 0; - } - ''' - - def post(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - ''' - FS.createDataFile('/', 'somefile.binary', [100, 1, 50, 25, 10, 77, 123], true, false); - ''' - ) - open(filename, 'w').write(src) - - Popen(['java', '-jar', CLOSURE_COMPILER, - '--compilation_level', 'ADVANCED_OPTIMIZATIONS', - '--formatting', 'PRETTY_PRINT', - '--variable_map_output_file', filename + '.vars', - '--js', filename, '--js_output_file', filename + '.cc.js'], stdout=PIPE, stderr=STDOUT).communicate() - assert not re.search('function \w\(', open(filename, 'r').read()) # closure generates this kind of stuff - functions with single letters. Normal doesn't. - src = open(filename + '.cc.js', 'r').read() - assert re.search('function \w\(', src) # see before - assert 'function _main()' not in src # closure should have wiped it out - shutil.move(filename, filename + '.old.js') - open(filename, 'w').write(src) - - self.do_run(src, '*closured*\ndata: 100,1,50,25\n', post_build=post) - def test_safe_heap(self): if not Settings.SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP') if Settings.USE_TYPED_ARRAYS == 2: return self.skip('It is ok to violate the load-store assumption with TA2') @@ -4615,13 +4680,15 @@ Child2:9 } ''' try: - def post(filename): - lines = open(filename, 'r').readlines() - lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines) - found_line_num = any(('//@line 7 "' in line) for line in lines) - found_filename = any(('src.cpp"\n' in line) for line in lines) - assert found_line_num, 'Must have debug info with the line number' - assert found_filename, 'Must have debug info with the filename' + post = r''' +def process(filename): + lines = open(filename, 'r').readlines() + lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines) + found_line_num = any(('//@line 7 "' in line) for line in lines) + found_filename = any(('src.cpp"\n' in line) for line in lines) + assert found_line_num, 'Must have debug info with the line number' + assert found_filename, 'Must have debug info with the filename' +''' self.do_run(src, '*nothingatall*', post_build=post) except Exception, e: # This test *should* fail @@ -4867,7 +4934,7 @@ Child2:9 # Generate tests for everything - def make_run(name=-1, compiler=-1, llvm_opts=0, embetter=0, quantum_size=0, typed_arrays=0, defaults=False): + def make_run(fullname, name=-1, compiler=-1, llvm_opts=0, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None): exec(''' class %s(T): def tearDown(self): @@ -4880,9 +4947,9 @@ class %s(T): os.chdir(self.get_dir()) # Ensure the directory exists and go there Building.COMPILER = %r - self.use_defaults = %d - if self.use_defaults: - Settings.load_defaults() + self.emcc_args = %s + if self.emcc_args is not None: + Settings.load(self.emcc_args) Building.LLVM_OPTS = 0 return @@ -4892,7 +4959,8 @@ class %s(T): # TODO: Move much of these to a init() function in shared.py, and reuse that Settings.USE_TYPED_ARRAYS = %d Settings.INVOKE_RUN = 1 - Settings.RELOOP = Settings.MICRO_OPTS = embetter + Settings.RELOOP = 0 # we only do them in the "o2" pass + Settings.MICRO_OPTS = embetter Settings.QUANTUM_SIZE = quantum_size Settings.ASSERTIONS = 1-embetter Settings.SAFE_HEAP = 1-(embetter and llvm_opts) @@ -4912,8 +4980,7 @@ class %s(T): Settings.BUILD_AS_SHARED_LIB = 0 Settings.RUNTIME_LINKED_LIBS = [] Settings.CATCH_EXIT_CODE = 0 - Settings.TOTAL_MEMORY = Settings.FAST_MEMORY = None - Settings.EMULATE_UNALIGNED_ACCESSES = Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2 + Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 if Settings.USE_TYPED_ARRAYS == 2: Settings.I64_MODE = 1 @@ -4921,18 +4988,20 @@ class %s(T): else: Settings.I64_MODE = 0 - if Settings.USE_TYPED_ARRAYS != 2 or Building.LLVM_OPTS == 2: - Settings.RELOOP = 0 # XXX Would be better to use this, but it isn't really what we test in these cases, and is very slow - Building.pick_llvm_opts(3, safe=Building.LLVM_OPTS != 2) TT = %s -''' % (fullname, fullname, fullname, compiler, defaults, llvm_opts, embetter, quantum_size, typed_arrays, fullname)) +''' % (fullname, fullname, fullname, compiler, str(emcc_args), llvm_opts, embetter, quantum_size, typed_arrays, fullname)) return TT # Make one run with the defaults - fullname = 'default' - exec(fullname + ' = make_run(compiler=CLANG, defaults=True)') + exec('default = make_run("default", compiler=CLANG, emcc_args=[])') + + # Make one run with -O1, with safe heap + exec('o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "SAFE_HEAP=1"])') + + # Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow) + exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "--closure", "0"])') # Make custom runs with various options for compiler, quantum, embetter, typed_arrays, llvm_opts in [ @@ -4942,14 +5011,11 @@ TT = %s (CLANG, 4, 0, 0, 1), (CLANG, 4, 1, 1, 0), (CLANG, 4, 1, 1, 1), - (CLANG, 4, 1, 2, 0), - (CLANG, 4, 1, 2, 1), - #(CLANG, 4, 1, 2, 2), ]: fullname = 's_%d_%d%s%s' % ( llvm_opts, embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays) ) - exec('%s = make_run(%r,%r,%d,%d,%d,%d)' % (fullname, fullname, compiler, llvm_opts, embetter, quantum, typed_arrays)) + exec('%s = make_run(fullname, %r,%r,%d,%d,%d,%d)' % (fullname, fullname, compiler, llvm_opts, embetter, quantum, typed_arrays)) del T # T is just a shape for the specific subclasses, we don't test it itself @@ -5188,7 +5254,7 @@ Options that are modified or new in %s include: def test_js_optimizer(self): input = open(path_from_root('tools', 'test-js-optimizer.js')).read() expected = open(path_from_root('tools', 'test-js-optimizer-output.js')).read() - output = Popen([NODE_JS, JS_OPTIMIZER, 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost', 'loopOptimizer'], + output = Popen([NODE_JS, JS_OPTIMIZER, 'hoistMultiples', 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost', 'loopOptimizer'], stdin=PIPE, stdout=PIPE).communicate(input)[0] self.assertIdentical(expected, output.replace('\n\n', '\n')) @@ -5237,7 +5303,7 @@ elif 'benchmark' in str(sys.argv): Building.COMPILER_TEST_OPTS = [] TEST_REPS = 10 - TOTAL_TESTS = 7 + TOTAL_TESTS = 8 tests_done = 0 total_times = map(lambda x: 0., range(TOTAL_TESTS)) @@ -5397,9 +5463,16 @@ elif 'benchmark' in str(sys.argv): ''' self.do_benchmark(src, [], 'final: 826:14324.', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1']) - def test_fasta(self): + def fasta(self, emcc_args=[]): src = open(path_from_root('tests', 'fasta.cpp'), 'r').read() - self.do_benchmark(src, ['2100000'], '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\nTCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\nAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\nGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\nCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\nGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\nGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\nTTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\nAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\nGCCTGGGCGA''') + self.do_benchmark(src, ['2100000'], '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\nTCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\nAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\nGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\nCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\nGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\nGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\nTTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\nAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\nGCCTGGGCGA''', + emcc_args=emcc_args) + + def test_fasta(self): + self.fasta() + + def test_fasta_t2(self): + self.fasta(emcc_args=['-s', 'USE_TYPED_ARRAYS=2', '-s', 'QUANTUM_SIZE=4']) def test_skinning(self): src = open(path_from_root('tests', 'skinning_test_no_simd.cpp'), 'r').read() diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index bf971951..665ba0db 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -357,6 +357,140 @@ function simplifyExpressionsPost(ast) { simplifyNotComps(ast); } +// Multiple blocks from the relooper are, in general, implemented by +// if (__label__ == x) { } else if .. +// and branching into them by +// if (condition) { __label__ == x } else .. +// We can hoist the multiple block into the condition, thus removing code and one 'if' check +function hoistMultiples(ast) { + ast[1].forEach(function(node, i) { + var type = node[0]; + if (type == 'defun') { + var statements = node[3]; + for (var i = 0; i < statements.length-1; i++) { + var modified = false; + var pre = statements[i]; + if (pre[0] != 'if') continue; + var post = statements[i+1]; + // Look into some block types. shell() will then recreate the shell that we looked into + var postInner = post; + var shell = function(x) { return x }; + while (true) { + /*if (postInner[0] == 'block') { + postInner = postInner[1][0]; + } else */if (postInner[0] == 'label') { + shell = (function(oldShell, oldPostInner) { + return function(x) { + return oldShell(['label', oldPostInner[1], x]); + }; + })(shell, postInner); + postInner = postInner[2]; + } else if (postInner[0] == 'do') { + shell = (function(oldShell, oldPostInner) { + return function(x) { + return oldShell(['do', copy(oldPostInner[1]), ['block', [x]]]); + } + })(shell, postInner);; + postInner = postInner[2][1][0]; + } else { + break; // give up + } + } + if (postInner[0] != 'if') continue; + // Look into this if, and its elseifs + while (postInner && postInner[0] == 'if') { + var cond = postInner[1]; + if (cond[0] == 'binary' && cond[1] == '==' && cond[2][0] == 'name' && cond[2][1] == '__label__') { + assert(cond[3][0] == 'num'); + // We have a valid Multiple check here. Try to hoist it, look for the source in |pre| and its else's + var labelNum = cond[3][1]; + var labelBlock = postInner[2]; + assert(labelBlock[0] == 'block'); + var found = false; + traverse(pre, function(preNode, preType) { + if (!found && preType == 'assign' && preNode[2][0] == 'name' && preNode[2][1] == '__label__') { + assert(preNode[3][0] == 'num'); + if (preNode[3][1] == labelNum) { + // That's it! Hoist away + found = true; + modified = true; + postInner[2] = ['block', []]; + return ['block', [preNode].concat(labelBlock[1])]; + } + } + }); + } + postInner = postInner[3]; // Proceed to look in the else clause + } + if (modified) { + statements[i] = shell(pre); + } + } + } + }); + + // Clear out empty ifs and blocks, and redundant blocks/stats + var more = true; + while (more) { + more = false; + ast[1].forEach(function(node, i) { + var type = node[0]; + if (type == 'defun') { + traverse(node, function(node, type) { + if (type == 'if' && node[2][0] == 'block' && node[2][1].length == 0) { + more = true; + if (node[2][2]) { // if there is an else, return that + return node[2][2]; + } else { + return emptyNode(); + } + } else if (type == 'block' && !node[1]) { + return emptyNode(); + } else if (type == 'block' && (node[1].length == 0 || (node[1].length == 1 && jsonCompare(node[1][0], emptyNode())))) { + more = true; + return emptyNode(); + } else if (type == 'block' && node[1].length == 1 && node[1][0][0] == 'block') { + more = true; + return node[1][0]; + } else if (type == 'stat' && node[1][0] == 'block') { + more = true; + return node[1]; + } else if (type == 'block') { + var pre = node[1].length; + node[1] = node[1].filter(function(blockItem) { return !jsonCompare(blockItem, emptyNode()) }); + if (node[1].length < pre) { + more = true; + return node; + } + } else if (type == 'defun' && node[3].length == 1 && node[3][0][0] == 'block') { + more = true; + node[3] = node[3][0][1]; + return node; + } else if (type == 'defun') { + var pre = node[3].length; + node[3] = node[3].filter(function(blockItem) { return !jsonCompare(blockItem, emptyNode()) }); + if (node[3].length < pre) { + more = true; + return node; + } + } else if (type == 'do' && node[1][0] == 'num' && jsonCompare(node[2], emptyNode())) { + more = true; + return emptyNode(); + } else if (type == 'label' && jsonCompare(node[2], emptyNode())) { + more = true; + return emptyNode(); + } else if (type == 'if' && jsonCompare(node[3], emptyNode())) { // empty else clauses + node[3] = null; + return node; + } + }); + } + }); + } +} + +// Simplifies loops +// WARNING: This assumes all loops and breaks/continues are labelled function loopOptimizer(ast) { // Remove unneeded labels and one-time (do while(0)) loops. It is convenient to do these both at once. function passTwo(ast) { @@ -442,6 +576,7 @@ var passes = { //removeUnneededLabelSettings: removeUnneededLabelSettings, simplifyExpressionsPre: simplifyExpressionsPre, simplifyExpressionsPost: simplifyExpressionsPost, + hoistMultiples: hoistMultiples, loopOptimizer: loopOptimizer }; @@ -457,16 +592,7 @@ if (metadata) setGeneratedFunctions(metadata); arguments.forEach(function(arg) { passes[arg](ast); }); -/* TODO: run only on generated functions (but, some passes look at globals...) -arguments.forEach(function(arg) { - ast[1].forEach(function(node, i) { - if (node[0] == 'defun' && isGenerated(node[1])) { - passes[arg](node); - } - }); -}); -*/ - +ast = srcToAst(astToSrc(ast)); // re-parse, to simplify a little print(astToSrc(ast)); if (metadata) print(metadata + '\n'); diff --git a/tools/shared.py b/tools/shared.py index 5b238aa4..adf3004e 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -269,18 +269,52 @@ def read_pgo_data(filename): class Settings: @classmethod def reset(self): - global Settings class Settings2: - reset = Settings.reset - load_defaults = Settings.load_defaults QUANTUM_SIZE = 4 - Settings = Settings2 + reset = Settings.reset - @classmethod - def load_defaults(self): - ''' Load the JS settings into Python ''' - settings = open(path_from_root('src', 'settings.js')).read().replace('var ', 'Settings.').replace('//', '#') - exec(settings) + # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings + @classmethod + def load(self, args): + # Load the JS defaults into python + settings = open(path_from_root('src', 'settings.js')).read().replace('var ', 'Settings.').replace('//', '#') + exec settings in globals() + + # Apply additional settings. First -O, then -s + for i in range(len(args)): + if args[i].startswith('-O'): + level = eval(args[i][2]) + Settings.apply_opt_level(level) + for i in range(len(args)): + if args[i] == '-s': + exec 'Settings.' + args[i+1] in globals() # execute the setting + + # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically + # the reverse of load_settings, except for -Ox which is relevant there but not here + @classmethod + def serialize(self): + ret = [] + for key, value in Settings.__dict__.iteritems(): + if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not + ret += ['-s', key + '=' + json.dumps(value)] + return ret + + @classmethod + def apply_opt_level(self, opt_level, noisy=False): + if opt_level >= 1: + Settings.ASSERTIONS = 0 + Settings.DISABLE_EXCEPTION_CATCHING = 1 + if opt_level >= 2: + Settings.RELOOP = 1 + if opt_level >= 3: + Settings.CORRECT_SIGNS = 0 + Settings.CORRECT_OVERFLOWS = 0 + Settings.CORRECT_ROUNDINGS = 0 + Settings.I64_MODE = 0 + Settings.DOUBLE_MODE = 0 + if noisy: print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)' + global Settings + Settings = Settings2 Settings.reset() # Building @@ -432,27 +466,21 @@ class Building: return ret @staticmethod - def emcc(filename, args=[], stdout=None, stderr=None, env=None): - try_delete(filename + '.o') - Popen([EMCC, filename] + args + ['-o', filename + '.o'], stdout=stdout, stderr=stderr, env=env).communicate()[0] - assert os.path.exists(filename + '.o'), 'Could not create bc file' + def emcc(filename, args=[], output_filename=None, stdout=None, stderr=None, env=None): + if output_filename is None: + output_filename = filename + '.o' + try_delete(output_filename) + Popen([EMCC, filename] + args + ['-o', output_filename], stdout=stdout, stderr=stderr, env=env).communicate() + assert os.path.exists(output_filename), 'emcc could not create output file' @staticmethod - def emscripten(filename, output_processor=None, append_ext=True, extra_args=[]): + def emscripten(filename, append_ext=True, extra_args=[]): # Add some headers by default. TODO: remove manually adding these in each test if '-H' not in extra_args: extra_args += ['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'] # Run Emscripten - exported_settings = {} - for setting in ['QUANTUM_SIZE', 'RELOOP', 'MICRO_OPTS', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'PGO', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'RUNTIME_LINKED_LIBS', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTION_CATCHING', 'TOTAL_MEMORY', 'FAST_MEMORY', 'EXCEPTION_DEBUG', 'PROFILE', 'I64_MODE', 'EMULATE_UNALIGNED_ACCESSES', 'CATCH_EXIT_CODE', 'USE_FHEAP']: - try: - value = eval('Settings.' + setting) - if value is not None: - exported_settings[setting] = value - except: - pass - settings = ['-s %s=%s' % (k, json.dumps(v)) for k, v in exported_settings.items()] + settings = Settings.serialize() compiler_output = timeout_run(Popen(['python', EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling') #print compiler_output @@ -460,9 +488,6 @@ class Building: if compiler_output is not None and 'Traceback' in compiler_output and 'in test_' in compiler_output: print compiler_output; assert 0 assert os.path.exists(filename + '.o.js') and len(open(filename + '.o.js', 'r').read()) > 0, 'Emscripten failed to generate .js: ' + str(compiler_output) - if output_processor is not None: - output_processor(open(filename + '.o.js').read()) - return filename + '.o.js' @staticmethod @@ -564,7 +589,7 @@ class Building: passes = [passes] input = open(filename, 'r').read() output, err = Popen([NODE_JS, JS_OPTIMIZER] + passes, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(input) - assert len(output) > 0, 'Error in js optimizer: ' + err + '\n\n' + output + assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + err + '\n\n' + output filename += '.jo.js' f = open(filename, 'w') f.write(output) diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js index d171c5b5..aa494a05 100644 --- a/tools/test-js-optimizer-output.js +++ b/tools/test-js-optimizer-output.js @@ -86,4 +86,82 @@ function maths() { check(95); __ZN6b2Vec2C1Ev($this1 + 76 | 0); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths"] +function hoisting() { + if ($i < $N) { + __label__ = 2; + callOther(); + } + pause(1); + $for_body3$$for_end$5 : do { + if ($i < $N) { + __label__ = 2; + while (true) { + break $for_body3$$for_end$5; + } + callOther(); + } else { + __label__ = 3; + } + } while (0); + pause(2); + do { + if ($i < $N) { + __label__ = 2; + if (callOther()) break; + } else { + __label__ = 3; + } + } while (0); + pause(3); + if ($i < $N) { + __label__ = 2; + callOther(); + } else { + __label__ = 3; + } + pause(4); + if ($i < $N) { + __label__ = 2; + callOther(); + } else { + __label__ = 3; + somethingElse(); + } + pause(5); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + somethingElse(); + } + if (__label__ == 55) { + callOther(); + } + pause(6); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + somethingElse(); + } +} +var FS = { + absolutePath: (function(relative, base) { + if (typeof relative !== "string") return null; + if (base === undefined) base = FS.currentPath; + if (relative && relative[0] == "/") base = ""; + var full = base + "/" + relative; + var parts = full.split("/").reverse(); + var absolute = [ "" ]; + while (parts.length) { + var part = parts.pop(); + if (part == "" || part == ".") {} else if (part == "..") { + if (absolute.length > 1) absolute.pop(); + } else { + absolute.push(part); + } + } + return absolute.length == 1 ? "/" : absolute.join("/"); + }) +}; +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting"] diff --git a/tools/test-js-optimizer.js b/tools/test-js-optimizer.js index 0665462b..8d10d222 100644 --- a/tools/test-js-optimizer.js +++ b/tools/test-js-optimizer.js @@ -88,5 +88,97 @@ function maths() { check(90+3+2); __ZN6b2Vec2C1Ev(((((((($this1 + 20 | 0 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths"] - +function hoisting() { + if ($i < $N) { + __label__ = 2; + } + if (__label__ == 2) { + callOther(); + } +// ok /* + pause(1); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + $for_body3$$for_end$5 : do { + if (__label__ == 2) { + while(true) { break $for_body3$$for_end$5 } + callOther(); + } + } while (0); + pause(2); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + cheez: do { + if (__label__ == 2) { + if (callOther()) break cheez; + } + } while (0); + pause(3); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 2) { + callOther(); + } + pause(4); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 2) { + callOther(); + } else if (__label__ == 3) { + somethingElse(); + } + pause(5); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 55) { + callOther(); + } else if (__label__ == 3) { + somethingElse(); + } + pause(6); + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 3) { + somethingElse(); + } +} +var FS = { + absolutePath: function(relative, base) { // Don't touch this! + if (typeof relative !== 'string') return null; + if (base === undefined) base = FS.currentPath; + if (relative && relative[0] == '/') base = ''; + var full = base + '/' + relative; + var parts = full.split('/').reverse(); + var absolute = ['']; + while (parts.length) { + var part = parts.pop(); + if (part == '' || part == '.') { + // Nothing. + } else if (part == '..') { + if (absolute.length > 1) absolute.pop(); + } else { + absolute.push(part); + } + } + return absolute.length == 1 ? '/' : absolute.join('/'); + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting"] |