diff --git a/tests/runner.py b/tests/runner.py index 404dd08eb689..f2884840051e 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -425,8 +425,8 @@ def hardcode_arguments(self, filename, args): js = open(filename).read() create_test_file(filename, js.replace('run();', 'run(%s + Module["arguments"]);' % str(args))) - def prep_ll_run(self, filename, ll_file, force_recompile=False, build_ll_hook=None): - # force_recompile = force_recompile or os.path.getsize(filename + '.o.ll') > 50000 + def prep_ll_file(self, output_file, input_file, force_recompile=False, build_ll_hook=None): + # force_recompile = force_recompile or os.path.getsize(filename + '.ll') > 50000 # If the file is big, recompile just to get ll_opts # Recompiling just for dfe in ll_opts is too costly @@ -446,43 +446,44 @@ def fix_target(ll_filename): with open(ll_filename, 'w') as f: f.write(contents) + output_obj = output_file + '.o' + output_ll = output_file + '.ll' + if force_recompile or build_ll_hook: - if ll_file.endswith(('.bc', '.o')): - if ll_file != filename + '.o': - shutil.copy(ll_file, filename + '.o') - Building.llvm_dis(filename) + if input_file.endswith(('.bc', '.o')): + if input_file != output_obj: + shutil.copy(input_file, output_obj) + Building.llvm_dis(output_obj, output_ll) else: - shutil.copy(ll_file, filename + '.o.ll') - fix_target(filename + '.o.ll') + shutil.copy(input_file, output_ll) + fix_target(output_ll) if build_ll_hook: - need_post = build_ll_hook(filename) - Building.llvm_as(filename) - shutil.move(filename + '.o.ll', filename + '.o.ll.pre') # for comparisons later - Building.llvm_dis(filename) + need_post = build_ll_hook(output_file) + Building.llvm_as(output_ll, output_obj) + shutil.move(output_ll, output_ll + '.pre') # for comparisons later + Building.llvm_dis(output_obj, output_ll) if build_ll_hook and need_post: - build_ll_hook(filename) - Building.llvm_as(filename) - shutil.move(filename + '.o.ll', filename + '.o.ll.post') # for comparisons later - Building.llvm_dis(filename) + build_ll_hook(output_file) + Building.llvm_as(output_ll, output_obj) + shutil.move(output_ll, output_ll + '.post') # for comparisons later + Building.llvm_dis(output_obj, output_ll) - Building.llvm_as(filename) + Building.llvm_as(output_ll, output_obj) else: - if ll_file.endswith('.ll'): - safe_copy(ll_file, filename + '.o.ll') - fix_target(filename + '.o.ll') - Building.llvm_as(filename) + if input_file.endswith('.ll'): + safe_copy(input_file, output_ll) + fix_target(output_ll) + Building.llvm_as(output_ll, output_obj) else: - safe_copy(ll_file, filename + '.o') + safe_copy(input_file, output_obj) + + return output_obj def get_emcc_args(self): # TODO(sbc): We should probably unify Building.COMPILER_TEST_OPTS and self.emcc_args return self.serialize_settings() + self.emcc_args + Building.COMPILER_TEST_OPTS - # Generate JS from ll - def ll_to_js(self, filename): - Building.emcc(filename + '.o', self.get_emcc_args(), filename + '.o.js') - # Build JavaScript code from source code def build(self, src, dirname, filename, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, @@ -501,7 +502,7 @@ def build(self, src, dirname, filename, main_file=None, # copy whole directory, and use a specific main .cpp file # (rmtree() fails on Windows if the current working directory is inside the tree.) if os.getcwd().startswith(os.path.abspath(dirname)): - os.chdir(os.path.join(dirname, '..')) + os.chdir(os.path.join(dirname, '..')) shutil.rmtree(dirname) shutil.copytree(src, dirname) shutil.move(os.path.join(dirname, main_file), filename) @@ -539,10 +540,10 @@ def build(self, src, dirname, filename, main_file=None, self.fail("Linkage error") # Finalize - self.prep_ll_run(filename, object_file, build_ll_hook=build_ll_hook) + self.prep_ll_file(filename, object_file, build_ll_hook=build_ll_hook) # BC => JS - self.ll_to_js(filename) + Building.emcc(object_file, self.get_emcc_args(), object_file + '.js') else: # "fast", new path: just call emcc and go straight to JS all_files = [filename] + additional_files + libraries @@ -976,15 +977,23 @@ def do_run(self, src, expected_output, args=[], output_nicerizer=None, basename = 'src.c' Building.COMPILER = to_cc(Building.COMPILER) - dirname = self.get_dir() - 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, + if no_build: + if src: + js_file = src + else: + js_file = basename + '.o.js' + else: + dirname = self.get_dir() + filename = os.path.join(dirname, basename) + self.build(src, dirname, filename, main_file=main_file, + additional_files=additional_files, libraries=libraries, + includes=includes, build_ll_hook=build_ll_hook, post_build=post_build) + js_file = filename + '.o.js' + self.assertExists(js_file) # Run in both JavaScript engines, if optimizing - significant differences there (typed arrays) js_engines = self.filtered_js_engines(js_engines) - js_file = filename + '.o.js' if len(js_engines) == 0: self.skipTest('No JS engine present to run this test with. Check %s and the paths therein.' % EM_CONFIG) if len(js_engines) > 1 and not self.use_all_engines: @@ -1007,32 +1016,12 @@ def do_run(self, src, expected_output, args=[], output_nicerizer=None, print('(test did not pass in JS engine: %s)' % engine) raise - # shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging - if self.save_JS: global test_index self.hardcode_arguments(js_file, args) shutil.copyfile(js_file, os.path.join(TEMP_DIR, str(test_index) + '.js')) test_index += 1 - # 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, force_recompile=False, - build_ll_hook=None, assert_returncode=None): - filename = os.path.join(self.get_dir(), 'src.cpp') - - self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook) - - self.ll_to_js(filename) - - self.do_run(None, - expected_output, - args, - no_build=True, - js_engines=js_engines, - output_nicerizer=output_nicerizer, - assert_returncode=assert_returncode) - def get_freetype_library(self): self.set_setting('DEAD_FUNCTIONS', self.get_setting('DEAD_FUNCTIONS') + ['_inflateEnd', '_inflate', '_inflateReset', '_inflateInit2_']) diff --git a/tests/test_core.py b/tests/test_core.py index 948a90c76d9d..dd98bb698e35 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -316,7 +316,7 @@ def test_cube2hash(self): for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'), ('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'), ('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]: - self.do_run('', 'hash value: ' + output, [text], no_build=True) + self.do_run('src.cpp.o.js', 'hash value: ' + output, [text], no_build=True) def test_unaligned(self): self.skipTest('LLVM marks the reads of s as fully aligned, making this test invalid') @@ -592,6 +592,17 @@ def test_getgep(self): # Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP self.do_run_in_out_file_test('tests', 'core', 'test_getgep') + # No compiling from C/C++ - just process an existing .o/.ll/.bc file. + def do_run_object(self, obj_file, expected_output=None, **kwargs): + js_file = os.path.basename(obj_file) + '.js' + Building.emcc(obj_file, self.get_emcc_args(), js_file) + self.do_run(js_file, expected_output, no_build=True, **kwargs) + + def do_ll_run(self, filename, expected_output=None, **kwargs): + output_base = os.path.basename(filename) + objfile = self.prep_ll_file(output_base, filename) + self.do_run_object(objfile, expected_output, **kwargs) + def test_multiply_defined_symbols(self): create_test_file('a1.c', 'int f() { return 1; }') create_test_file('a2.c', 'void x() {}') @@ -617,7 +628,7 @@ def test_multiply_defined_symbols(self): Building.link_to_object(['main.c.o', 'liba.a', 'libb.a'], 'all.o') - self.do_ll_run('all.o', 'result: 1') + self.do_run_object('all.o', 'result: 1') def test_if(self): self.do_run_in_out_file_test('tests', 'core', 'test_if') @@ -1065,9 +1076,9 @@ def test_exceptions_3(self): print('0') self.do_run(src, 'Caught C string: a c string\nDone.', ['0']) print('1') - self.do_run(src, 'Caught exception: std::exception\nDone.', ['1'], no_build=True) + self.do_run(None, 'Caught exception: std::exception\nDone.', ['1'], no_build=True) print('2') - self.do_run(src, 'Caught exception: Hello\nDone.', ['2'], no_build=True) + self.do_run(None, 'Caught exception: Hello\nDone.', ['2'], no_build=True) def test_exceptions_white_list(self): self.set_setting('DISABLE_EXCEPTION_CATCHING', 2) @@ -5269,9 +5280,11 @@ def test_constglobalunion(self): def test_fannkuch(self): results = [(1, 0), (2, 1), (3, 2), (4, 4), (5, 7), (6, 10), (7, 16), (8, 22)] + src = open(path_from_root('tests', 'fannkuch.cpp')).read() + self.build(src, self.get_dir(), 'fannkuch.cpp') for i, j in results: - src = open(path_from_root('tests', 'fannkuch.cpp')).read() - self.do_run(src, 'Pfannkuchen(%d) = %d.' % (i, j), [str(i)], no_build=i > 1) + print(i, j) + self.do_run('fannkuch.cpp.o.js', 'Pfannkuchen(%d) = %d.' % (i, j), [str(i)], no_build=True) def test_raytrace(self): # TODO: Should we remove this test? @@ -5287,6 +5300,7 @@ def test_fasta(self): (50, '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''')] old = self.emcc_args + orig_src = open(path_from_root('tests', 'fasta.cpp')).read() def test(extra_args): self.emcc_args = old + extra_args @@ -5294,10 +5308,11 @@ def test(extra_args): self.set_setting('PRECISE_F32', precision) for t in ['float', 'double']: print(precision, t) - src = open(path_from_root('tests', 'fasta.cpp')).read().replace('double', t) - for i, j in results: - self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i > 1) - shutil.copyfile('src.cpp.o.js', '%d_%s.js' % (precision, t)) + src = orig_src.replace('double', t) + self.build(src, self.get_dir(), 'fasta.cpp') + for arg, output in results: + self.do_run('fasta.cpp.o.js', output, [str(arg)], lambda x, err: x.replace('\n', '*'), no_build=True) + shutil.copyfile('fasta.cpp.o.js', '%d_%s.js' % (precision, t)) test([]) @@ -5317,12 +5332,12 @@ def test_dlmalloc(self): src = open(path_from_root('system', 'lib', 'dlmalloc.c')).read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c')).read() self.do_run(src, '*1,0*', ['200', '1']) - self.do_run(src, '*400,0*', ['400', '400'], no_build=True) + self.do_run(None, '*400,0*', ['400', '400'], no_build=True) # Linked version src = open(path_from_root('tests', 'dlmalloc_test.c')).read() self.do_run(src, '*1,0*', ['200', '1']) - self.do_run(src, '*400,0*', ['400', '400'], no_build=True) + self.do_run(None, '*400,0*', ['400', '400'], no_build=True) # TODO: do this in other passes too, passing their opts into emcc if self.emcc_args == []: @@ -5331,8 +5346,8 @@ def test_dlmalloc(self): try_delete('src.cpp.o.js') run_process([PYTHON, EMCC, path_from_root('tests', 'dlmalloc_test.c'), '-s', 'TOTAL_MEMORY=128MB', '-o', 'src.cpp.o.js'], stdout=PIPE, stderr=self.stderr_redirect) - self.do_run('x', '*1,0*', ['200', '1'], no_build=True) - self.do_run('x', '*400,0*', ['400', '400'], no_build=True) + self.do_run(None, '*1,0*', ['200', '1'], no_build=True) + self.do_run(None, '*400,0*', ['400', '400'], no_build=True) # The same for new and all its variants src = open(path_from_root('tests', 'new.cpp')).read() @@ -5589,7 +5604,7 @@ def test_freetype(self): includes=[path_from_root('tests', 'freetype', 'include')]) print('[issue 324 case 3]') - self.do_run('', + self.do_run(None, open(path_from_root('tests', 'freetype', 'ref_4.txt')).read(), ['font.ttf', 'ea', '40', '32', '0'], no_build=True) @@ -5821,12 +5836,12 @@ def test_python(self): print('lto:', lto) if lto == 1: self.emcc_args += ['--llvm-lto', '1'] - self.do_ll_run(bitcode, pyoutput, args=['-S', '-c', pyscript]) + self.do_run_object(bitcode, pyoutput, args=['-S', '-c', pyscript]) def test_lifetime(self): self.do_ll_run(path_from_root('tests', 'lifetime.ll'), 'hello, world!\n') if '-O1' in self.emcc_args or '-O2' in self.emcc_args: - assert 'a18' not in open('src.cpp.o.js').read(), 'lifetime stuff and their vars must be culled' + assert 'a18' not in open('lifetime.ll.o.js').read(), 'lifetime stuff and their vars must be culled' # Test cases in separate files. Note that these files may contain invalid .ll! # They are only valid enough for us to read for test purposes, not for llvm-as @@ -5980,12 +5995,12 @@ def test_autodebug_bitcode(self): # Autodebug the code def do_autodebug(filename): - Building.llvm_dis(filename) - output = run_process([PYTHON, AUTODEBUGGER, filename + '.o.ll', filename + '.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).stdout + Building.llvm_dis(filename + '.o', filename + '.ll') + output = run_process([PYTHON, AUTODEBUGGER, filename + '.ll', filename + '.auto.ll'], stdout=PIPE, stderr=self.stderr_redirect).stdout assert 'Success.' in output, output # rebuild .bc # TODO: use code in do_autodebug_post for this - self.prep_ll_run(filename, filename + '.o.ll.ll', force_recompile=True) + self.prep_ll_file(filename, filename + '.auto.ll', force_recompile=True) # Run a test that should work, generating some code test_path = path_from_root('tests', 'core', 'test_structs') @@ -5998,7 +6013,7 @@ def do_autodebug(filename): do_autodebug(filename) # Compare to each other, and to expected output - self.do_ll_run(filename + '.o.ll.ll', 'AD:-1,1') + self.do_ll_run(filename + '.auto.ll', 'AD:-1,1') # Test using build_ll_hook src = ''' @@ -6183,31 +6198,28 @@ def test_dead_functions(self): } ''' - def test(expected, args=[], no_build=False): - self.do_run(src, expected, args=args, no_build=no_build) - return open('src.cpp.o.js').read() - # Sanity check that it works and the dead function is emitted - js = test('*1*', ['x']) + self.do_run(src, '*1*', args=['x']) + js = open('src.cpp.o.js').read() if self.run_name in ['default', 'asm1', 'asm2g']: assert 'function _unused($' in js - test('*2*', no_build=True) + self.do_run(None, '*2*', no_build=True) # Kill off the dead function, and check a code path using it aborts self.set_setting('DEAD_FUNCTIONS', ['_unused']) - test('*2*') - test('abort(', args=['x'], no_build=True) + self.do_run(src, '*2*') + self.do_run(None, 'abort(', args=['x'], no_build=True) # Kill off a library function, check code aborts self.set_setting('DEAD_FUNCTIONS', ['_printf']) - test('abort(') - test('abort(', args=['x'], no_build=True) + self.do_run(src, 'abort(') + self.do_run(None, 'abort(', args=['x'], no_build=True) def test_response_file(self): response_data = '-o %s/response_file.o.js %s' % (self.get_dir(), path_from_root('tests', 'hello_world.cpp')) create_test_file('rsp_file', response_data.replace('\\', '\\\\')) run_process([PYTHON, EMCC, "@rsp_file"] + self.emcc_args) - self.do_run('', 'hello, world', basename='response_file', no_build=True) + self.do_run('response_file.o.js', 'hello, world', no_build=True) def test_linker_response_file(self): objfile = 'response_file.o' @@ -6218,7 +6230,7 @@ def test_linker_response_file(self): response_data = objfile + ' --export=foo' create_test_file('rsp_file', response_data.replace('\\', '\\\\')) run_process([PYTHON, EMCC, "-Wl,@rsp_file", '-o', 'response_file.o.js'] + self.emcc_args) - self.do_run('', 'hello, world', basename='response_file', no_build=True) + self.do_run('response_file.o.js', 'hello, world', no_build=True) def test_exported_response(self): src = r''' diff --git a/tools/shared.py b/tools/shared.py index 09bc8dde316b..1273ca138d2a 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -2093,28 +2093,18 @@ def llvm_opt(filename, opts, out=None): return target @staticmethod - def llvm_dis(input_filename, output_filename=None): + def llvm_dis(input_filename, output_filename): # LLVM binary ==> LLVM assembly - if output_filename is None: - # use test runner conventions - output_filename = input_filename + '.o.ll' - input_filename = input_filename + '.o' try_delete(output_filename) output = run_process([LLVM_DIS, input_filename, '-o', output_filename], stdout=PIPE).stdout assert os.path.exists(output_filename), 'Could not create .ll file: ' + output - return output_filename @staticmethod - def llvm_as(input_filename, output_filename=None): + def llvm_as(input_filename, output_filename): # LLVM assembly ==> LLVM binary - if output_filename is None: - # use test runner conventions - output_filename = input_filename + '.o' - input_filename = input_filename + '.o.ll' try_delete(output_filename) output = run_process([LLVM_AS, input_filename, '-o', output_filename], stdout=PIPE).stdout assert os.path.exists(output_filename), 'Could not create bc file: ' + output - return output_filename @staticmethod def parse_symbols(output, include_internal=False):