From df6c1629d85270ecb69b289b911f51c0a9914214 Mon Sep 17 00:00:00 2001 From: Douglas Crosher Date: Mon, 13 Apr 2015 23:19:56 +1000 Subject: [PATCH 01/26] Assert that EM_SAVE_DIR is not set when running the parallel tests. --- tests/parallel_test_core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/parallel_test_core.py b/tests/parallel_test_core.py index c501c4ce39ec6..dc663e054e1bd 100755 --- a/tests/parallel_test_core.py +++ b/tests/parallel_test_core.py @@ -4,6 +4,8 @@ import os, sys, subprocess, multiprocessing, threading, time from runner import test_modes, PYTHON, path_from_root +assert not os.environ.get('EM_SAVE_DIR') + # run slower ones first, to optimize total time optimal_order = ['asm3i', 'asm1i', 'asm2nn', 'asm3', 'asm2', 'asm2g', 'asm2f', 'asm1', 'default'] assert set(optimal_order) == set(test_modes), 'need to update the list of slowest modes' From 920cd7cf6917ba37f8e0fe75b8e0b063776fd786 Mon Sep 17 00:00:00 2001 From: Douglas Crosher Date: Tue, 14 Apr 2015 12:24:31 +1000 Subject: [PATCH 02/26] Update AUTHORS, and comment the change. --- AUTHORS | 1 + tests/parallel_test_core.py | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 96fe5f2879362..9dd94ede0111f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -87,6 +87,7 @@ a license to everyone to use it as detailed in LICENSE.) * Manfred Manik Nerurkar (copyright owned by MADE, GmbH) * Joseph Gentle * Douglas T. Crosher (copyright owned by Mozilla Foundation) +* Douglas T. Crosher (copyright owned by Scieneer Pty Ltd) * Soeren Balko * Ryan Kelly (ryan@rfk.id.au) * Michael Lelli diff --git a/tests/parallel_test_core.py b/tests/parallel_test_core.py index dc663e054e1bd..06edf8e598fc0 100755 --- a/tests/parallel_test_core.py +++ b/tests/parallel_test_core.py @@ -4,6 +4,7 @@ import os, sys, subprocess, multiprocessing, threading, time from runner import test_modes, PYTHON, path_from_root +# Need separate directories to avoid the parallel tests clashing. assert not os.environ.get('EM_SAVE_DIR') # run slower ones first, to optimize total time From 86bfa325c2c219e3ef33dc839d2d8bc1848cdd74 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 14 Apr 2015 16:06:17 +0700 Subject: [PATCH 03/26] Remove asm2x86 references. --- tests/test_core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index f5682b69e6c6b..00158e2d414df 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1357,7 +1357,6 @@ class MyException def test_exceptions_2(self): if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') - if self.run_name == 'asm2x86': return self.skip('TODO') Settings.DISABLE_EXCEPTION_CATCHING = 0 @@ -1370,7 +1369,6 @@ def test_exceptions_2(self): def test_exceptions_3(self): if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') - if self.run_name == 'asm2x86': return self.skip('TODO') Settings.DISABLE_EXCEPTION_CATCHING = 0 From e638be6049ad66679e47083bc3366e688cd7c830 Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Tue, 14 Apr 2015 14:48:25 +0100 Subject: [PATCH 04/26] Remove dead code --- tests/runner.py | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index fbb081a7a9f1d..d7f2ca976680b 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -146,30 +146,17 @@ def ll_to_js(self, filename, extra_emscripten_args, post_build): if emcc_args is None: emcc_args = [] - if emcc_args is None: # legacy testing mode, no longer used - Building.emscripten(filename, append_ext=True, extra_args=extra_emscripten_args) - if post1: - exec post1 in locals() - shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js') - process(filename + '.o.js') - if post2: post2(filename + '.o.js') - else: - transform_args = [] - if post1: - transform_filename = os.path.join(self.get_dir(), 'transform.py') - transform = open(transform_filename, 'w') - transform.write(''' -import sys -sys.path += [%r] -''' % path_from_root('')) - transform.write(post1) - transform.write(''' -process(sys.argv[1]) -''') - transform.close() - transform_args = ['--js-transform', "%s %s" % (PYTHON, transform_filename)] - Building.emcc(filename + '.o.ll', Settings.serialize() + emcc_args + transform_args + Building.COMPILER_TEST_OPTS, filename + '.o.js') - if post2: post2(filename + '.o.js') + transform_args = [] + if post1: + transform_filename = os.path.join(self.get_dir(), 'transform.py') + transform = open(transform_filename, 'w') + transform.write('\nimport sys\nsys.path += [%r]\n' % path_from_root('')) + transform.write(post1) + transform.write('\nprocess(sys.argv[1])\n') + transform.close() + transform_args = ['--js-transform', "%s %s" % (PYTHON, transform_filename)] + Building.emcc(filename + '.o.ll', Settings.serialize() + emcc_args + transform_args + Building.COMPILER_TEST_OPTS, filename + '.o.js') + if post2: post2(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=[], post_build=None): From ebd4923a4d26172c91fb641a28700ecf53cabca0 Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Tue, 14 Apr 2015 15:17:44 +0100 Subject: [PATCH 05/26] emcc_args is asserted as not None in setUp, remove checks --- tests/test_core.py | 195 +++------------------------------------------ 1 file changed, 10 insertions(+), 185 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index f5682b69e6c6b..a65e6e853798c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -24,8 +24,6 @@ def test_hello_world(self): assert 'EMSCRIPTEN_GENERATED_FUNCTIONS' not in src, 'must not emit this unneeded internal thing' def test_intvars(self): - if self.emcc_args == None: return self.skip('needs ta2') - test_path = path_from_root('tests', 'core', 'test_intvars') src, output = (test_path + s for s in ('.in', '.out')) @@ -422,8 +420,6 @@ def zzztest_nested_struct_varargs(self): self.do_run_from_file(src, output) def test_i32_mul_precise(self): - if self.emcc_args == None: return self.skip('needs ta2') - test_path = path_from_root('tests', 'core', 'test_i32_mul_precise') src, output = (test_path + s for s in ('.in', '.out')) @@ -470,16 +466,12 @@ def test_line_endings(self): self.build(open(path_from_root('tests', 'hello_world.cpp')).read(), self.get_dir(), self.in_dir('hello_world.cpp')) def test_literal_negative_zero(self): - if self.emcc_args == None: return self.skip('needs emcc') - test_path = path_from_root('tests', 'core', 'test_literal_negative_zero') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_llvm_intrinsics(self): - if self.emcc_args == None: return self.skip('needs ta2') - Settings.PRECISE_I64_MATH = 2 # for bswap64 test_path = path_from_root('tests', 'core', 'test_llvm_intrinsics') @@ -497,8 +489,6 @@ def test_bswap64(self): self.do_run_from_file(src, output) def test_sha1(self): - if self.emcc_args == None: return self.skip('needs ta2') - self.do_run(open(path_from_root('tests', 'sha1.c')).read(), 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6') def test_asmjs_unknown_emscripten(self): @@ -506,7 +496,6 @@ def test_asmjs_unknown_emscripten(self): self.do_run(open(path_from_root('tests', 'asmjs-unknown-emscripten.c')).read(), '') def test_cube2md5(self): - if self.emcc_args == None: return self.skip('needs emcc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for accurate math') self.emcc_args += ['--embed-file', 'cube2md5.txt'] shutil.copyfile(path_from_root('tests', 'cube2md5.txt'), os.path.join(self.get_dir(), 'cube2md5.txt')) @@ -748,8 +737,6 @@ def test_unsigned(self): self.do_run(src, '*255*\n*65535*\n*-1*\n*-1*\n*-1*') def test_bitfields(self): - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design - test_path = path_from_root('tests', 'core', 'test_bitfields') src, output = (test_path + s for s in ('.in', '.out')) @@ -767,7 +754,6 @@ def test_closebitcasts(self): self.do_run_from_file(src, output) def test_fast_math(self): - if self.emcc_args is None: return self.skip('requires emcc') Building.COMPILER_TEST_OPTS += ['-ffast-math'] test_path = path_from_root('tests', 'core', 'test_fast_math') @@ -817,7 +803,6 @@ def test_math_hyperbolic(self): self.do_run(src, expected) def test_math_lgamma(self): - if self.emcc_args is None: return self.skip('requires emcc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for accurate math') test_path = path_from_root('tests', 'math', 'lgamma') @@ -838,8 +823,6 @@ def test_rounding(self): self.do_run_from_file(src, output) def test_fcvt(self): - if self.emcc_args is None: return self.skip('requires emcc') - test_path = path_from_root('tests', 'core', 'test_fcvt') src, output = (test_path + s for s in ('.in', '.out')) @@ -1035,7 +1018,6 @@ def test_mallocstruct(self): self.do_run(self.gen_struct_src.replace('{{gen_struct}}', '(S*)malloc(sizeof(S))').replace('{{del_struct}}', 'free'), '*51,62*') def test_newstruct(self): - if self.emcc_args is None: return self.skip('requires emcc') self.do_run(self.gen_struct_src.replace('{{gen_struct}}', 'new S').replace('{{del_struct}}', 'delete'), '*51,62*') def test_addr_of_stacked(self): @@ -1113,16 +1095,12 @@ def test_assert(self): self.do_run_from_file(src, output) def test_libcextra(self): - if self.emcc_args is None: return self.skip('needs emcc for libcextra') - test_path = path_from_root('tests', 'core', 'test_libcextra') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_regex(self): - if self.emcc_args is None: return self.skip('needs emcc for libcextra') - test_path = path_from_root('tests', 'core', 'test_regex') src, output = (test_path + s for s in ('.in', '.out')) @@ -1270,7 +1248,6 @@ def test_setjmp_noleak(self): def test_exceptions(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") - if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') Settings.EXCEPTION_DEBUG = 1 @@ -1356,7 +1333,6 @@ class MyException self.do_run(src, 'Throw...Construct...Caught...Destruct...Throw...Construct...Copy...Caught...Destruct...Destruct...') def test_exceptions_2(self): - if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') if self.run_name == 'asm2x86': return self.skip('TODO') Settings.DISABLE_EXCEPTION_CATCHING = 0 @@ -1369,7 +1345,6 @@ def test_exceptions_2(self): self.do_run_from_file(src, output) def test_exceptions_3(self): - if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') if self.run_name == 'asm2x86': return self.skip('TODO') Settings.DISABLE_EXCEPTION_CATCHING = 0 @@ -1410,8 +1385,6 @@ def test_exceptions_3(self): self.do_run(src, 'Caught exception: Hello\nDone.', ['2'], no_build=True) def test_exceptions_white_list(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.DISABLE_EXCEPTION_CATCHING = 2 Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"] Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified @@ -1447,8 +1420,6 @@ def test_exceptions_white_list(self): assert fake_size - disabled_size < 0.007*size, [disabled_size, fake_size] def test_exceptions_white_list_2(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.DISABLE_EXCEPTION_CATCHING = 2 Settings.EXCEPTION_CATCHING_WHITELIST = ["_main"] Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified @@ -1458,8 +1429,6 @@ def test_exceptions_white_list_2(self): self.do_run_from_file(src, output) def test_exceptions_uncaught(self): - if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') - Settings.DISABLE_EXCEPTION_CATCHING = 0 src = r''' @@ -1497,8 +1466,6 @@ def test_exceptions_uncaught(self): self.do_run(src, 'success') def test_exceptions_typed(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.DISABLE_EXCEPTION_CATCHING = 0 self.emcc_args += ['-s', 'SAFE_HEAP=0'] # Throwing null will cause an ignorable null pointer access. @@ -1508,8 +1475,6 @@ def test_exceptions_typed(self): self.do_run_from_file(src, output) def test_exceptions_virtual_inheritance(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.DISABLE_EXCEPTION_CATCHING = 0 test_path = path_from_root('tests', 'core', 'test_exceptions_virtual_inheritance') @@ -1530,7 +1495,6 @@ def test_exceptions_multi(self): self.do_run_from_file(src, output) def test_exceptions_std(self): - if self.emcc_args is None: return self.skip('requires emcc') Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.ERROR_ON_UNDEFINED_SYMBOLS = 1 self.emcc_args += ['-s', 'SAFE_HEAP=0'] @@ -1541,8 +1505,6 @@ def test_exceptions_std(self): self.do_run_from_file(src, output) def test_exceptions_alias(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.DISABLE_EXCEPTION_CATCHING = 0 test_path = path_from_root('tests', 'core', 'test_exceptions_alias') src, output = (test_path + s for s in ('.c', '.out')) @@ -1580,8 +1542,6 @@ def test_exceptions_primary(self): self.do_run_from_file(src, output) def test_bad_typeid(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.ERROR_ON_UNDEFINED_SYMBOLS = 1 Settings.DISABLE_EXCEPTION_CATCHING = 0 @@ -1608,7 +1568,6 @@ class Polymorphic {virtual void member(){}}; ''', 'exception caught: std::bad_typeid') def test_exit_stack(self): - if self.emcc_args is None: return self.skip('requires emcc') if Settings.ASM_JS: return self.skip('uses report_stack without exporting') Settings.INLINING_LIMIT = 50 @@ -1669,24 +1628,18 @@ def test_inherit(self): self.do_run_from_file(src, output) def test_isdigit_l(self): - if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') - test_path = path_from_root('tests', 'core', 'test_isdigit_l') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_iswdigit(self): - if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') - test_path = path_from_root('tests', 'core', 'test_iswdigit') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_polymorph(self): - if self.emcc_args is None: return self.skip('requires emcc') - test_path = path_from_root('tests', 'core', 'test_polymorph') src, output = (test_path + s for s in ('.in', '.out')) @@ -1721,8 +1674,6 @@ def test_complex(self): value = real 1.25 imag 0.00''', force_c=True) def test_segfault(self): - if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults') - Settings.SAFE_HEAP = 1 for addr in ['0', 'new D2()']: @@ -1756,8 +1707,6 @@ def test_segfault(self): def test_safe_dyncalls(self): if Settings.ASM_JS: return self.skip('asm does not support missing function stack traces') if Settings.SAFE_HEAP: return self.skip('safe heap warning will appear instead') - if self.emcc_args is None: return self.skip('need libc') - Settings.SAFE_DYNCALLS = 1 for cond, body, work in [(True, True, False), (True, False, False), (False, True, True), (False, False, False)]: @@ -1787,24 +1736,18 @@ def test_safe_dyncalls(self): self.do_run(src, ('dyncall error: vi', 'missing function: _ZN2D14doItEv') if not work else 'all good') def test_dynamic_cast(self): - if self.emcc_args is None: return self.skip('need libcxxabi') - test_path = path_from_root('tests', 'core', 'test_dynamic_cast') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_dynamic_cast_b(self): - if self.emcc_args is None: return self.skip('need libcxxabi') - test_path = path_from_root('tests', 'core', 'test_dynamic_cast_b') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_dynamic_cast_2(self): - if self.emcc_args is None: return self.skip('need libcxxabi') - test_path = path_from_root('tests', 'core', 'test_dynamic_cast_2') src, output = (test_path + s for s in ('.in', '.out')) @@ -1840,8 +1783,6 @@ def test_funcptr_namecollide(self): self.do_run_from_file(src, output, force_c=True) def test_emptyclass(self): - if self.emcc_args is None: return self.skip('requires emcc') - test_path = path_from_root('tests', 'core', 'test_emptyclass') src, output = (test_path + s for s in ('.in', '.out')) @@ -1858,24 +1799,18 @@ def test_rename(self): self.do_run(src, 'success', force_c=True) def test_alloca_stack(self): - if self.emcc_args is None: return # too slow in other modes - test_path = path_from_root('tests', 'core', 'test_alloca_stack') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output, force_c=True) def test_stack_byval(self): - if self.emcc_args is None: return # too slow in other modes - test_path = path_from_root('tests', 'core', 'test_stack_byval') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_stack_varargs(self): - if self.emcc_args is None: return # too slow in other modes - Settings.INLINING_LIMIT = 50 Settings.TOTAL_STACK = 1024 @@ -1885,7 +1820,6 @@ def test_stack_varargs(self): self.do_run_from_file(src, output) def test_stack_varargs2(self): - if self.emcc_args is None: return # too slow in other modes Settings.TOTAL_STACK = 1536 src = r''' #include @@ -1969,7 +1903,6 @@ def test_stack_void(self): self.do_run_from_file(src, output) def test_life(self): - if self.emcc_args is None: return self.skip('need c99') self.emcc_args += ['-std=c99'] src = open(path_from_root('tests', 'life.c'), 'r').read() self.do_run(src, '''-------------------------------- @@ -2033,8 +1966,6 @@ def test_conststructs(self): self.do_run_from_file(src, output) def test_bigarray(self): - if self.emcc_args is None: return self.skip('need ta2 to compress type data on zeroinitializers') - test_path = path_from_root('tests', 'core', 'test_bigarray') src, output = (test_path + s for s in ('.in', '.out')) @@ -2117,8 +2048,6 @@ def test_pystruct(self): self.do_run(src, '*0,0,0,4,8,12,16,20*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,20*') def test_ptrtoint(self): - if self.emcc_args is None: return self.skip('requires emcc') - runner = self def check_warnings(output): runner.assertEquals(filter(lambda line: 'Warning' in line, output.split('\n')).__len__(), 4) @@ -2129,7 +2058,6 @@ def check_warnings(output): self.do_run_from_file(src, output, output_processor=check_warnings) def test_sizeof(self): - if self.emcc_args is None: return self.skip('requires emcc') # Has invalid writes between printouts Settings.SAFE_HEAP = 0 @@ -2275,7 +2203,7 @@ def test_memorygrowth(self): self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*') win = open('src.cpp.o.js').read() - if self.emcc_args and '-O2' in self.emcc_args: + if '-O2' in self.emcc_args: # Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized) code_start = 'var TOTAL_MEMORY' fail = fail[fail.find(code_start):] @@ -2313,8 +2241,6 @@ def test_ssr(self): # struct self-ref self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''') def test_tinyfuncstr(self): - if self.emcc_args is None: return self.skip('requires emcc') - test_path = path_from_root('tests', 'core', 'test_tinyfuncstr') src, output = (test_path + s for s in ('.in', '.out')) @@ -2527,7 +2453,7 @@ def test_structbyval(self): # part 2: make sure we warn about mixing c and c++ calling conventions here - if not (self.emcc_args is None or self.emcc_args == []): return # Optimized code is missing the warning comments + if self.emcc_args != []: return # Optimized code is missing the warning comments header = r''' struct point @@ -2589,7 +2515,6 @@ def test_structbyval(self): print >> sys.stderr, 'skipping C/C++ conventions warning check, since not i386-pc-linux-gnu' def test_stdlibs(self): - if self.emcc_args is None: return self.skip('requires emcc') # safe heap prints a warning that messes up our output. Settings.SAFE_HEAP = 0 src = ''' @@ -2663,8 +2588,6 @@ def test_stdlibs(self): self.do_run(src, '*1*', force_c=True) def test_strtoll_hex(self): - if self.emcc_args is None: return self.skip('requires emcc') - # tests strtoll for hex strings (0x...) test_path = path_from_root('tests', 'core', 'test_strtoll_hex') src, output = (test_path + s for s in ('.in', '.out')) @@ -2672,8 +2595,6 @@ def test_strtoll_hex(self): self.do_run_from_file(src, output) def test_strtoll_dec(self): - if self.emcc_args is None: return self.skip('requires emcc') - # tests strtoll for decimal strings (0x...) test_path = path_from_root('tests', 'core', 'test_strtoll_dec') src, output = (test_path + s for s in ('.in', '.out')) @@ -2681,8 +2602,6 @@ def test_strtoll_dec(self): self.do_run_from_file(src, output) def test_strtoll_bin(self): - if self.emcc_args is None: return self.skip('requires emcc') - # tests strtoll for binary strings (0x...) test_path = path_from_root('tests', 'core', 'test_strtoll_bin') src, output = (test_path + s for s in ('.in', '.out')) @@ -2690,8 +2609,6 @@ def test_strtoll_bin(self): self.do_run_from_file(src, output) def test_strtoll_oct(self): - if self.emcc_args is None: return self.skip('requires emcc') - # tests strtoll for decimal strings (0x...) test_path = path_from_root('tests', 'core', 'test_strtoll_oct') src, output = (test_path + s for s in ('.in', '.out')) @@ -2734,7 +2651,6 @@ def test_atexit(self): self.do_run_from_file(src, output) def test_pthread_specific(self): - if self.emcc_args is None: return self.skip('requires emcc') src = open(path_from_root('tests', 'pthread', 'specific.c'), 'r').read() expected = open(path_from_root('tests', 'pthread', 'specific.c.txt'), 'r').read() self.do_run(src, expected, force_c=True) @@ -2819,8 +2735,6 @@ def test_statics(self): self.do_run_from_file(src, output) def test_copyop(self): - if self.emcc_args is None: return self.skip('requires emcc') - # clang generated code is vulnerable to this, as it uses # memcpy for assignments, with hardcoded numbers of bytes # (llvm-gcc copies items one by one). See QUANTUM_SIZE in @@ -2857,16 +2771,12 @@ def test_memset(self): self.do_run_from_file(src, output) def test_getopt(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') - test_path = path_from_root('tests', 'core', 'test_getopt') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output, args=['-t', '12', '-n', 'foobar']) def test_getopt_long(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') - test_path = path_from_root('tests', 'core', 'test_getopt_long') src, output = (test_path + s for s in ('.in', '.out')) @@ -3886,7 +3796,6 @@ def test_rand(self): self.do_run(src, expected) def test_strtod(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for accurate math') src = r''' @@ -4000,8 +3909,6 @@ def test_vprintf(self): self.do_run_from_file(src, output) def test_vsnprintf(self): - if self.emcc_args is None: return self.skip('needs i64 math') - test_path = path_from_root('tests', 'core', 'test_vsnprintf') src, output = (test_path + s for s in ('.in', '.out')) @@ -4020,8 +3927,6 @@ def test_perrar(self): self.do_run_from_file(src, output) def test_atoX(self): - if self.emcc_args is None: return self.skip('requires ta2') - test_path = path_from_root('tests', 'core', 'test_atoX') src, output = (test_path + s for s in ('.in', '.out')) @@ -4034,13 +3939,11 @@ def test_strstr(self): self.do_run_from_file(src, output) def test_fnmatch(self): - if self.emcc_args is None: return self.skip('requires linking in libc++') test_path = path_from_root('tests', 'core', 'fnmatch') src, output = (test_path + s for s in ('.c', '.out')) self.do_run_from_file(src, output) def test_sscanf(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for accurate math') test_path = path_from_root('tests', 'core', 'test_sscanf') @@ -4177,7 +4080,7 @@ def test_langinfo(self): def test_files(self): self.banned_js_engines = [SPIDERMONKEY_ENGINE] # closure can generate variables called 'gc', which pick up js shell stuff - if self.emcc_args is not None and '-O2' in self.emcc_args: + if '-O2' in self.emcc_args: self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff self.emcc_args = filter(lambda x: x != '-g', self.emcc_args) # ensure we test --closure 1 --memory-init-file 1 (-g would disable closure) self.emcc_args += ["-s", "CHECK_HEAP_ALIGN=0"] # disable heap align check here, it mixes poorly with closure @@ -4215,7 +4118,7 @@ def process(filename): try_delete(mem_file) self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n \ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'), post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) - if self.emcc_args and '-O2' in self.emcc_args: + if '-O2' in self.emcc_args: assert os.path.exists(mem_file) def test_files_m(self): @@ -4259,7 +4162,7 @@ def test_fwrite_0(self): test_path = path_from_root('tests', 'core', 'test_fwrite_0') src, output = (test_path + s for s in ('.in', '.out')) - orig_args = self.emcc_args if self.emcc_args else [] + orig_args = self.emcc_args for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]: self.emcc_args = orig_args + mode self.do_run_from_file(src, output) @@ -4274,7 +4177,6 @@ def test_fgetc_ungetc(self): self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS]) def test_fgetc_unsigned(self): - if self.emcc_args is None: return self.skip('requires emcc') src = r''' #include int main() { @@ -4288,7 +4190,6 @@ def test_fgetc_unsigned(self): self.do_run(src, '*234\n') def test_fgets_eol(self): - if self.emcc_args is None: return self.skip('requires emcc') src = r''' #include char buf[32]; @@ -4312,7 +4213,6 @@ def test_fgets_eol(self): self.do_run(src, 'SUCCESS\n') def test_fscanf(self): - if self.emcc_args is None: return self.skip('requires emcc') open(os.path.join(self.get_dir(), 'three_numbers.txt'), 'w').write('''-1 0.1 -.1''') src = r''' #include @@ -4337,8 +4237,6 @@ def test_fscanf(self): self.do_run(src, 'match = 3\nx = -1.0, y = 0.1, z = -0.1\n') def test_fscanf_2(self): - if self.emcc_args is None: return self.skip('requires emcc') - open('a.txt', 'w').write('''1/2/3 4/5/6 7/8/9 ''') self.emcc_args += ['--embed-file', 'a.txt'] @@ -4366,7 +4264,6 @@ def test_fscanf_2(self): ''', 'fscanf test\n9\n') def test_fileno(self): - if self.emcc_args is None: return self.skip('requires emcc') open(os.path.join(self.get_dir(), 'empty.txt'), 'w').write('') src = r''' #include @@ -4388,7 +4285,6 @@ def test_fileno(self): self.do_run(src, '4\n3\n-1\n') def test_readdir(self): - if self.emcc_args is None: return self.skip('requires emcc') self.banned_js_engines = [V8_ENGINE] # stderr printing limitations in v8 src = open(path_from_root('tests', 'dirent', 'test_readdir.c'), 'r').read() self.do_run(src, '''SIGILL: Illegal instruction @@ -4496,7 +4392,6 @@ def test_utf(self): self.do_run_from_file(src, output) def test_utf32(self): - if self.emcc_args is None: return self.skip('need libc for wcslen()') if not self.is_emscripten_abi(): return self.skip('this test uses inline js, which requires asmjs-unknown-emscripten') self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.') @@ -4509,7 +4404,6 @@ def test_utf8(self): self.do_run(open(path_from_root('tests', 'utf8.cpp')).read(), 'OK.') def test_wprintf(self): - if self.emcc_args is None: return self.skip('requires libcxx') test_path = path_from_root('tests', 'core', 'test_wprintf') src, output = (test_path + s for s in ('.c', '.out')) orig_args = self.emcc_args @@ -4518,24 +4412,18 @@ def test_wprintf(self): self.do_run_from_file(src, output) def test_direct_string_constant_usage(self): - if self.emcc_args is None: return self.skip('requires libcxx') - test_path = path_from_root('tests', 'core', 'test_direct_string_constant_usage') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_std_cout_new(self): - if self.emcc_args is None: return self.skip('requires emcc') - test_path = path_from_root('tests', 'core', 'test_std_cout_new') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_istream(self): - if self.emcc_args is None: return self.skip('requires libcxx') - test_path = path_from_root('tests', 'core', 'test_istream') src, output = (test_path + s for s in ('.in', '.out')) @@ -4562,7 +4450,6 @@ def process(filename): Settings.INCLUDE_FULL_LIBRARY = 0 def test_fs_nodefs_rw(self): - if self.emcc_args is None: return self.skip('requires emcc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') src = open(path_from_root('tests', 'fs', 'test_nodefs_rw.c'), 'r').read() self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS]) @@ -4573,25 +4460,21 @@ def test_fs_trackingdelegate(self): self.do_run_from_file(src, out) def test_fs_writeFile(self): - if self.emcc_args is None: return self.skip('requires emcc') self.emcc_args += ['-s', 'DISABLE_EXCEPTION_CATCHING=1'] # see issue 2334 src = path_from_root('tests', 'fs', 'test_writeFile.cc') out = path_from_root('tests', 'fs', 'test_writeFile.out') self.do_run_from_file(src, out) def test_fs_emptyPath(self): - if self.emcc_args is None: return self.skip('requires emcc') src = path_from_root('tests', 'fs', 'test_emptyPath.c') out = path_from_root('tests', 'fs', 'test_emptyPath.out') self.do_run_from_file(src, out) def test_fs_append(self): - if self.emcc_args is None: return self.skip('requires emcc') src = open(path_from_root('tests', 'fs', 'test_append.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_fs_mmap(self): - if self.emcc_args is None: return self.skip('requires emcc') orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS']: src = path_from_root('tests', 'fs', 'test_mmap.c') @@ -4670,7 +4553,6 @@ def test_unistd_login(self): def test_unistd_unlink(self): self.clear() - if self.emcc_args is None: return self.skip('requires emcc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: @@ -4719,7 +4601,6 @@ def test_unistd_io(self): self.clear() if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') if self.run_name == 'o2': return self.skip('non-asm optimized builds can fail with inline js') - if self.emcc_args is None: return self.skip('requires emcc') orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read() @@ -4728,7 +4609,6 @@ def test_unistd_io(self): self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_misc(self): - if self.emcc_args is None: return self.skip('requires emcc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: @@ -4813,7 +4693,6 @@ def test_atomic(self): self.do_run_from_file(src, output) def test_atomic_cxx(self): - if self.emcc_args is None: return self.skip('needs emcc') test_path = path_from_root('tests', 'core', 'test_atomic_cxx') src, output = (test_path + s for s in ('.cpp', '.txt')) Building.COMPILER_TEST_OPTS += ['-std=c++11'] @@ -4834,8 +4713,6 @@ def test_netinet_in(self): def test_iostream(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") - if self.emcc_args is None: return self.skip('needs ta2 and emcc') - src = ''' #include @@ -4850,15 +4727,12 @@ def test_iostream(self): self.do_run(src, 'hello world\n77.\n') def test_stdvec(self): - if self.emcc_args is None: return self.skip('requires emcc') - test_path = path_from_root('tests', 'core', 'test_stdvec') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_random_device(self): - if self.emcc_args is None: return self.skip('requires emcc') Building.COMPILER_TEST_OPTS += ['-std=c++11'] test_path = path_from_root('tests', 'core', 'test_random_device') @@ -4867,8 +4741,6 @@ def test_random_device(self): self.do_run_from_file(src, output) def test_reinterpreted_ptrs(self): - if self.emcc_args is None: return self.skip('needs emcc and libc') - test_path = path_from_root('tests', 'core', 'test_reinterpreted_ptrs') src, output = (test_path + s for s in ('.in', '.out')) @@ -4954,8 +4826,6 @@ def test_jansson(self): self.do_run(src, 'value\narray_item1\narray_item2\narray_item3\n3\n3.00\n2.20\nJansson: Node with ID `0` not found. Context has `10` nodes.\n0\nJansson: No JSON context.\njansson!') def test_js_libraries(self): - if self.emcc_args == None: return self.skip('needs emcc') - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' #include extern "C" { @@ -5001,7 +4871,6 @@ def test_unicode_js_library(self): self.do_run(open(os.path.join(self.get_dir(), 'main.cpp'), 'r').read(), u'Unicode snowman \u2603 says hello!') def test_constglobalunion(self): - if self.emcc_args is None: return self.skip('needs emcc') self.emcc_args += ['-s', 'EXPORT_ALL=1'] self.do_run(r''' @@ -5044,7 +4913,6 @@ def test_fannkuch(self): self.do_run(src, 'Pfannkuchen(%d) = %d.' % (i,j), [str(i)], no_build=i>1) def test_raytrace(self): - if self.emcc_args is None: return self.skip('requires emcc') # TODO: Should we remove this test? return self.skip('Relies on double value rounding, extremely sensitive') @@ -5053,7 +4921,6 @@ def test_raytrace(self): self.do_run(src, output, ['3', '16'])#, build_ll_hook=self.do_autodebug) def test_fasta(self): - if self.emcc_args is None: return self.skip('requires emcc') results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''), (50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ] for precision in [0, 1, 2]: @@ -5070,8 +4937,6 @@ def test_whets(self): self.do_run(open(path_from_root('tests', 'whets.cpp')).read(), 'Single Precision C Whetstone Benchmark') def test_dlmalloc(self): - if self.emcc_args is None: self.emcc_args = [] # dlmalloc auto-inclusion is only done if we use emcc - self.banned_js_engines = [NODE_JS] # slower, and fail on 64-bit Settings.CORRECT_SIGNS = 1 Settings.TOTAL_MEMORY = 128*1024*1024 # needed with typed arrays @@ -5107,7 +4972,6 @@ def test_dlmalloc(self): self.do_run(src.replace('{{{ NEW }}}', new).replace('{{{ DELETE }}}', delete), '*1,0*') def test_dlmalloc_partial(self): - if self.emcc_args is None: return self.skip('only emcc will link in dlmalloc') # present part of the symbols of dlmalloc, not all src = open(path_from_root('tests', 'new.cpp')).read().replace('{{{ NEW }}}', 'new int').replace('{{{ DELETE }}}', 'delete') + ''' void * @@ -5120,7 +4984,7 @@ def test_dlmalloc_partial(self): self.do_run(src, 'new 4!\n*1,0*') def test_dlmalloc_partial_2(self): - if self.emcc_args is None or 'SAFE_HEAP' in str(self.emcc_args) or 'CHECK_HEAP_ALIGN' in str(self.emcc_args): return self.skip('only emcc will link in dlmalloc, and we do unsafe stuff') + if 'SAFE_HEAP' in str(self.emcc_args) or 'CHECK_HEAP_ALIGN' in str(self.emcc_args): return self.skip('only emcc will link in dlmalloc, and we do unsafe stuff') # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. test_path = path_from_root('tests', 'core', 'test_dlmalloc_partial_2') @@ -5129,7 +4993,6 @@ def test_dlmalloc_partial_2(self): self.do_run_from_file(src, output) def test_libcxx(self): - if self.emcc_args is None: return self.skip('requires emcc') self.do_run(open(path_from_root('tests', 'hashtest.cpp')).read(), 'june -> 30\nPrevious (in alphabetical order) is july\nNext (in alphabetical order) is march') @@ -5151,8 +5014,6 @@ def test_typeid(self): self.do_run_from_file(src, output) def test_static_variable(self): - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # LLVM mixes i64 and i8 in the guard check - test_path = path_from_root('tests', 'core', 'test_static_variable') src, output = (test_path + s for s in ('.in', '.out')) @@ -5165,8 +5026,6 @@ def test_fakestat(self): self.do_run_from_file(src, output) def test_mmap(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.TOTAL_MEMORY = 128*1024*1024 test_path = path_from_root('tests', 'core', 'test_mmap') @@ -5176,7 +5035,6 @@ def test_mmap(self): self.do_run_from_file(src, output, force_c=True) def test_mmap_file(self): - if self.emcc_args is None: return self.skip('requires emcc') for extra_args in [[], ['--no-heap-copy']]: self.emcc_args += ['--embed-file', 'data.dat'] + extra_args @@ -5186,12 +5044,10 @@ def test_mmap_file(self): self.do_run(src, '*\ndata from the file .\nfrom the file ......\n*\n') def test_cubescript(self): - if self.emcc_args is None: return self.skip('requires emcc') if self.run_name == 'o2': self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code... # Overflows happen in hash loop Settings.CORRECT_OVERFLOWS = 1 @@ -5311,7 +5167,6 @@ def test_gcc_unmangler(self): self.do_run(open(path_from_root('third_party', 'gcc_demangler.c')).read(), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj']) def test_lua(self): - if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') if self.emcc_args: self.emcc_args = ['-g1'] + self.emcc_args @@ -5346,7 +5201,6 @@ def get_freetype(self): os.path.join('objs', '.libs', 'libfreetype.a')) def test_freetype(self): - if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix') assert 'asm2g' in test_modes @@ -5407,7 +5261,6 @@ def process(filename): def test_sqlite(self): # gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c - if self.emcc_args is None: return self.skip('Very slow without ta2, and we would also need to include dlmalloc manually without emcc') if not self.is_emscripten_abi(): return self.skip('fails on x86 due to a legalization issue on llvm 3.3') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO FIXME') self.banned_js_engines = [NODE_JS] # OOM in older node @@ -5417,7 +5270,6 @@ def test_sqlite(self): Settings.CORRECT_SIGNS = 1 Settings.CORRECT_OVERFLOWS = 0 Settings.CORRECT_ROUNDINGS = 0 - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff Settings.DISABLE_EXCEPTION_CATCHING = 1 Settings.EXPORTED_FUNCTIONS += ['_sqlite3_open', '_sqlite3_close', '_sqlite3_exec', '_sqlite3_free', '_callback']; if Settings.ASM_JS == 1 and '-g' in self.emcc_args: @@ -5438,7 +5290,7 @@ def test_sqlite(self): force_c=True) def test_zlib(self): - if self.emcc_args is not None and '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly + if '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage assert 'asm2g' in test_modes @@ -5462,9 +5314,6 @@ def test_zlib(self): force_c=True) def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long - if self.emcc_args is None: return self.skip('requires emcc') - if Building.LLVM_OPTS and self.emcc_args is None: Settings.SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore - Settings.DEAD_FUNCTIONS = ['__ZSt9terminatev'] # Note: this is also a good test of per-file and per-line changes (since we have multiple files, and correct specific lines) @@ -5508,8 +5357,6 @@ def test(): assert old.count('tempBigInt') > new.count('tempBigInt') def test_poppler(self): - if self.emcc_args is None: return self.skip('very slow, we only do this in emcc runs') - Settings.CORRECT_OVERFLOWS = 1 Settings.CORRECT_SIGNS = 1 Settings.NO_EXIT_RUNTIME = 1 @@ -5564,8 +5411,6 @@ def process(filename): #, build_ll_hook=self.do_autodebug) def test_openjpeg(self): - if self.emcc_args is None: return self.skip('needs libc for getopt') - Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default Settings.CORRECT_SIGNS = 1 @@ -5694,7 +5539,6 @@ def clean(text): do_test() def test_python(self): - if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') if not self.is_emscripten_abi(): return self.skip('fails on not asmjs-unknown-emscripten') # FIXME @@ -5721,8 +5565,6 @@ def test_python(self): self.do_ll_run(bitcode, pyoutput, args=['-S', '-c', pyscript]) def test_lifetime(self): - if self.emcc_args is None: return self.skip('test relies on emcc opts') - 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(os.path.join(self.get_dir(), 'src.cpp.o.js')).read(), 'lifetime stuff and their vars must be culled' @@ -5971,7 +5813,7 @@ def process(filename): self.do_run_from_file(src, output, post_build=post) - if self.emcc_args is not None and ('-O2' in self.emcc_args or self.is_emterpreter()): + if '-O2' in self.emcc_args or self.is_emterpreter(): print 'with closure' self.emcc_args += ['--closure', '1'] self.do_run_from_file(src, output, post_build=post) @@ -6132,8 +5974,6 @@ def test_asm_pgo(self): assert open('pgoed.js').read() == open('pgoed2.js').read() def test_exported_response(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = r''' #include #include @@ -6157,8 +5997,6 @@ def test_exported_response(self): assert 'other_function' in open('src.cpp.o.js').read() def test_add_function(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.INVOKE_RUN = 0 Settings.RESERVED_FUNCTION_POINTERS = 1 @@ -6256,7 +6094,6 @@ def test_demangle_stacks(self): self.do_run_from_file(src, output) def test_tracing(self): - if self.emcc_args is None: return self.skip('requires emcc') Building.COMPILER_TEST_OPTS += ['--tracing'] test_path = path_from_root('tests', 'core', 'test_tracing') @@ -6265,7 +6102,6 @@ def test_tracing(self): self.do_run_from_file(src, output) def test_embind(self): - if self.emcc_args is None: return self.skip('requires emcc') Building.COMPILER_TEST_OPTS += ['--bind'] src = r''' @@ -6287,7 +6123,6 @@ def test_embind(self): self.do_run(src, 'abs(-10): 10\nabs(-11): 11'); def test_embind_2(self): - if self.emcc_args is None: return self.skip('requires emcc') Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] open('post.js', 'w').write(''' Module.print('lerp ' + Module.lerp(100, 200, 66) + '.'); @@ -6306,8 +6141,6 @@ def test_embind_2(self): self.do_run(src, 'lerp 166'); def test_scriptaclass(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.EXPORT_BINDINGS = 1 header_filename = os.path.join(self.get_dir(), 'header.h') @@ -6355,7 +6188,7 @@ def process(filename): ''' # XXX disable due to possible v8 bug -- self.do_run(src, '*166*\n*ok*', post_build=post) - if self.emcc_args is not None and '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly + if '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right # Way 2: use CppHeaderParser @@ -6554,8 +6387,6 @@ def post3(filename): ''', post_build=(post2, post3)) def test_scriptaclass_2(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.EXPORT_BINDINGS = 1 header_filename = os.path.join(self.get_dir(), 'header.h') @@ -6600,8 +6431,6 @@ def process(filename): self.do_run(src, '|hello|43|world|41|', post_build=post) def test_webidl(self): - if self.emcc_args is None: return self.skip('requires emcc') - if self.run_name == 'asm2': self.emcc_args += ['--closure', '1', '-g1'] # extra testing output = Popen([PYTHON, path_from_root('tools', 'webidl_binder.py'), @@ -6631,7 +6460,7 @@ def post(filename): def test_typeinfo(self): return self.skip('non-fastcomp is deprecated and fails in 3.5') # RUNTIME_TYPE_INFO - if self.emcc_args is not None and self.emcc_args != []: return self.skip('full LLVM opts optimize out all the code that uses the type') + if self.emcc_args != []: return self.skip('full LLVM opts optimize out all the code that uses the type') Settings.RUNTIME_TYPE_INFO = 1 Settings.NO_EXIT_RUNTIME = 1 @@ -6931,7 +6760,6 @@ def test_emscripten_log(self): if Settings.ASM_JS: # XXX Does not work in SpiderMonkey since callstacks cannot be captured when running in asm.js, see https://bugzilla.mozilla.org/show_bug.cgi?id=947996 self.banned_js_engines = [SPIDERMONKEY_ENGINE] - if self.emcc_args is None: return self.skip('This test needs libc.') if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') self.do_run('#define RUN_FROM_JS_SHELL\n' + open(path_from_root('tests', 'emscripten_log', 'emscripten_log.cpp')).read(), "Success!") @@ -7077,7 +6905,6 @@ def test_float_literals(self): self.do_run_from_file(path_from_root('tests', 'test_float_literals.cpp'), path_from_root('tests', 'test_float_literals.out')) def test_exit_status(self): - if self.emcc_args is None: return self.skip('need emcc') src = r''' #include #include @@ -7101,11 +6928,9 @@ def test_exit_status(self): self.do_run(src, 'hello, world!\ncleanup\nI see exit status: 118') def test_minmax(self): - if self.emcc_args == None: return self.skip('needs emcc') self.do_run(open(path_from_root('tests', 'test_minmax.c')).read(), 'NAN != NAN\nSuccess!') def test_locale(self): - if self.emcc_args is None: return self.skip('needs emcc') self.do_run_from_file(path_from_root('tests', 'test_locale.c'), path_from_root('tests', 'test_locale.out')) def test_64bit_return_value(self): From 7717b8596896eb50a7f740fb5ab385999954610b Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Tue, 14 Apr 2015 15:40:07 +0100 Subject: [PATCH 06/26] Add documentation of randomN option --- tests/runner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/runner.py b/tests/runner.py index d7f2ca976680b..b09a2bc75b2ca 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -851,6 +851,10 @@ def get_bullet_library(runner_core, use_cmake): python tests/runner.py ALL.test_hello_world +You can run a random set of N tests with a command like + + python tests/runner.py random50 + Debugging: You can run EM_SAVE_DIR=1 python tests/runner.py ALL.test_hello_world From 9fce3beed63344e5053eeffa1bceef3c5b0ab788 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 14 Apr 2015 21:23:07 +0700 Subject: [PATCH 07/26] Begin to untangle the compiler: Remove intertyper, analyzer & more. This is the start of removing the actual "old" compiler that has been deprecated and no longer works. --- src/analyzer.js | 1773 --------------------------------------------- src/compiler.js | 74 +- src/intertyper.js | 1226 ------------------------------- src/jsifier.js | 157 +--- 4 files changed, 12 insertions(+), 3218 deletions(-) delete mode 100644 src/analyzer.js delete mode 100644 src/intertyper.js diff --git a/src/analyzer.js b/src/analyzer.js deleted file mode 100644 index 453921657793e..0000000000000 --- a/src/analyzer.js +++ /dev/null @@ -1,1773 +0,0 @@ -//"use strict"; - -// Analyze intertype data. Calculates things that are necessary in order -// to do the final conversion into JavaScript later, for example, -// properties of variables, loop structures of functions, etc. - -var VAR_NATIVE = 'native'; -var VAR_NATIVIZED = 'nativized'; -var VAR_EMULATED = 'emulated'; - -var ENTRY_IDENT = toNiceIdent('%0'); - -function recomputeLines(func) { - func.lines = func.labels.map(function(label) { return label.lines }).reduce(concatenator, []); -} - -// Handy sets - -var BRANCH_INVOKE = set('branch', 'invoke'); -var LABEL_ENDERS = set('branch', 'return', 'switch'); -var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic'); -var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam'); -var SHADOW_FLIP = { i64: 'double', double: 'i64' }; //, i32: 'float', float: 'i32' }; - -// Analyzer - -function analyzer(data, sidePass) { - //B.start('analyzer'); - var mainPass = !sidePass; - - var item = { items: data }; - var data = item; - - var newTypes = {}; - - // Gather - // Single-liners - ['globalVariable', 'functionStub', 'unparsedFunction', 'unparsedGlobals', 'unparsedTypes', 'alias'].forEach(function(intertype) { - var temp = splitter(item.items, function(item) { return item.intertype == intertype }); - item.items = temp.leftIn; - item[intertype + 's'] = temp.splitOut; - }); - var temp = splitter(item.items, function(item) { return item.intertype == 'type' }); - item.items = temp.leftIn; - temp.splitOut.forEach(function(type) { - //dprint('types', 'adding defined type: ' + type.name_); - Types.types[type.name_] = type; - newTypes[type.name_] = 1; - if (QUANTUM_SIZE === 1) { - Types.fatTypes[type.name_] = copy(type); - } - }); - - // Functions & labels - item.functions = []; - var currLabelFinished = false; // Sometimes LLVM puts a branch in the middle of a label. We need to ignore all lines after that. - item.items.sort(function(a, b) { return a.lineNum - b.lineNum }); - for (var i = 0; i < item.items.length; i++) { - var subItem = item.items[i]; - assert(subItem.lineNum); - if (subItem.intertype == 'function') { - item.functions.push(subItem); - subItem.endLineNum = null; - subItem.lines = []; // We will fill in the function lines after the legalizer, since it can modify them - subItem.labels = []; - subItem.forceEmulated = false; - - // no explicit 'entry' label in clang on LLVM 2.8 - most of the time, but not all the time! - so we add one if necessary - if (item.items[i+1].intertype !== 'label') { - item.items.splice(i+1, 0, { - intertype: 'label', - ident: ENTRY_IDENT, - lineNum: subItem.lineNum + '.5' - }); - } - } else if (subItem.intertype == 'functionEnd') { - item.functions.slice(-1)[0].endLineNum = subItem.lineNum; - } else if (subItem.intertype == 'label') { - item.functions.slice(-1)[0].labels.push(subItem); - subItem.lines = []; - currLabelFinished = false; - } else if (item.functions.length > 0 && item.functions.slice(-1)[0].endLineNum === null) { - // Internal line - if (!currLabelFinished) { - item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label? - if (subItem.intertype in LABEL_ENDERS) { - currLabelFinished = true; - } - } else { - print('// WARNING: content after a branch in a label, line: ' + subItem.lineNum); - } - } else { - throw 'ERROR: what is this? ' + dump(subItem); - } - } - delete item.items; - - // CastAway - try to remove bitcasts of double<-->i64, which LLVM sometimes generates unnecessarily - // (load a double, convert to i64, use as i64). - // We optimize this by checking if there are such bitcasts. If so we create a shadow - // variable that is of the other type, and use that in the relevant places. (As SSA, this is valid, and - // variable elimination later will remove the double load if it is no longer needed.) - // - // Note that aside from being an optimization, this is needed for correctness in some cases: If code - // assumes it can bitcast a double to an i64 and back and forth without loss, that may be violated - // due to NaN canonicalization. - function castAway() { - item.functions.forEach(function(func) { - var has = false; - func.labels.forEach(function(label) { - var lines = label.lines; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP) { - has = true; - } - } - }); - if (!has) return; - // there are integer<->floating-point bitcasts, create shadows for everything - var shadowed = {}; - func.labels.forEach(function(label) { - var lines = label.lines; - var i = 0; - while (i < lines.length) { - var lines = label.lines; - var line = lines[i]; - if (line.intertype == 'load' && line.type in SHADOW_FLIP) { - if (line.pointer.intertype != 'value') { i++; continue } // TODO - shadowed[line.assignTo] = 1; - var shadow = line.assignTo + '$$SHADOW'; - var flip = SHADOW_FLIP[line.type]; - lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase - tokens: null, - indent: 2, - lineNum: line.lineNum + 0.5, - assignTo: shadow, - intertype: 'load', - pointerType: flip + '*', - type: flip, - valueType: flip, - pointer: { - intertype: 'value', - ident: line.pointer.ident, - type: flip + '*' - }, - align: line.align, - ident: line.ident - }); - // note: no need to update func.lines, it is generated in a later pass - i++; - } - i++; - } - }); - // use shadows where possible - func.labels.forEach(function(label) { - var lines = label.lines; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP && line.ident in shadowed) { - var shadow = line.ident + '$$SHADOW'; - line.params[0].ident = shadow; - line.params[0].type = line.type; - line.type2 = line.type; - } - } - }); - }); - } - - // Legalize LLVM unrealistic types into realistic types. - // - // With full LLVM optimizations, it can generate types like i888 which do not exist in - // any actual hardware implementation, but are useful during optimization. LLVM then - // legalizes these types into real ones during code generation. Sadly, there is no LLVM - // IR pass to legalize them, which would have been useful and nice from a design perspective. - // The LLVM community is also not interested in receiving patches to implement that - // functionality, since it would duplicate existing code from the code generation - // component. Therefore, we implement legalization here in Emscripten. - // - // Currently we just legalize completely unrealistic types into bundles of i32s, and just - // the most common instructions that can be involved with such types: load, store, shifts, - // trunc and zext. - function legalizer() { - // Legalization - function getLegalVars(base, bits, allowLegal) { - bits = bits || 32; // things like pointers are all i32, but show up as 0 bits from getBits - if (allowLegal && bits <= 32) return [{ intertype: 'value', ident: base + ('i' + bits in Compiletime.INT_TYPES ? '' : '$0'), bits: bits, type: 'i' + bits }]; - if (isNumber(base)) return getLegalLiterals(base, bits); - if (base[0] == '{') { - warnOnce('seeing source of illegal data ' + base + ', likely an inline struct - assuming zeroinit'); - return getLegalLiterals('0', bits); - } - var ret = new Array(Math.ceil(bits/32)); - var i = 0; - if (base == 'zeroinitializer' || base == 'undef') base = 0; - while (bits > 0) { - ret[i] = { intertype: 'value', ident: base ? base + '$' + i : '0', bits: Math.min(32, bits), type: 'i' + Math.min(32, bits) }; - bits -= 32; - i++; - } - return ret; - } - function getLegalLiterals(text, bits) { - var parsed = parseArbitraryInt(text, bits); - var ret = new Array(Math.ceil(bits/32)); - var i = 0; - while (bits > 0) { - ret[i] = { intertype: 'value', ident: (parsed[i]|0).toString(), bits: Math.min(32, bits), type: 'i' + Math.min(32, bits) }; // resign all values - bits -= 32; - i++; - } - return ret; - } - function getLegalStructuralParts(value) { - return value.params.slice(0); - } - function getLegalParams(params, bits) { - return params.map(function(param) { - var value = param.value || param; - if (isNumber(value.ident)) { - return getLegalLiterals(value.ident, bits); - } else if (value.intertype == 'structvalue') { - return getLegalStructuralParts(value).map(function(part) { - part.bits = part.type.substr(1); // can be some nested IR, like LLVM calls - return part; - }); - } else { - return getLegalVars(value.ident, bits); - } - }); - } - // Uses the right factor to multiply line numbers by so that they fit in between - // the line[i] and the line after it - function interpLines(lines, i, toAdd) { - var prev = i >= 0 ? lines[i].lineNum : -1; - var next = (i < lines.length-1) ? lines[i+1].lineNum : (lines[i].lineNum + 0.5); - var factor = (next - prev)/(4*toAdd.length+3); - for (var k = 0; k < toAdd.length; k++) { - toAdd[k].lineNum = prev + ((k+1)*factor); - assert(k == 0 || toAdd[k].lineNum > toAdd[k-1].lineNum); - } - } - function removeAndAdd(lines, i, toAdd) { - var item = lines[i]; - interpLines(lines, i, toAdd); - Array.prototype.splice.apply(lines, [i, 1].concat(toAdd)); - if (i > 0) assert(lines[i].lineNum > lines[i-1].lineNum); - if (i + toAdd.length < lines.length) assert(lines[i + toAdd.length - 1].lineNum < lines[i + toAdd.length].lineNum); - return toAdd.length; - } - function legalizeFunctionParameters(params) { - var i = 0; - while (i < params.length) { - var param = params[i]; - if (param.intertype == 'value' && isIllegalType(param.type)) { - var toAdd = getLegalVars(param.ident, getBits(param.type)).map(function(element) { - return { - intertype: 'value', - type: 'i' + element.bits, - ident: element.ident, - byval: 0 - }; - }); - Array.prototype.splice.apply(params, [i, 1].concat(toAdd)); - i += toAdd.length; - continue; - } else if (param.intertype == 'structvalue') { - // 'flatten' out the struct into scalars - var toAdd = param.params; - toAdd.forEach(function(param) { - param.byval = 0; - }); - Array.prototype.splice.apply(params, [i, 1].concat(toAdd)); - continue; // do not increment i; proceed to process the new params - } - i++; - } - } - function fixUnfolded(item) { - // Unfolded items may need some correction to work properly in the global scope - if (item.intertype in MATHOPS) { - item.op = item.intertype; - item.intertype = 'mathop'; - } - } - data.functions.forEach(function(func) { - // Legalize function params - legalizeFunctionParameters(func.params); - // Legalize lines in labels - var tempId = 0; - func.labels.forEach(function(label) { - if (dcheck('legalizer')) dprint('zz legalizing: \n' + dump(label.lines)); - var i = 0, bits; - while (i < label.lines.length) { - var item = label.lines[i]; - var value = item; - // Check if we need to legalize here, and do some trivial legalization along the way - var isIllegal = false; - walkInterdata(item, function(item) { - if (item.intertype == 'getelementptr' || (item.intertype == 'call' && item.ident in LLVM.INTRINSICS_32)) { - // Turn i64 args into i32 - for (var i = 0; i < item.params.length; i++) { - if (item.params[i].type == 'i64') item.params[i].type = 'i32'; - } - } else if (item.intertype == 'inttoptr') { - var input = item.params[0]; - if (input.type == 'i64') input.type = 'i32'; // inttoptr can only care about 32 bits anyhow since pointers are 32-bit - } - if (isIllegalType(item.valueType) || isIllegalType(item.type)) { - isIllegal = true; - } else if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) { - isIllegal = true; // storing an entire structure is illegal - } else if (item.intertype == 'mathop' && item.op == 'trunc' && isIllegalType(item.params[1].ident)) { // trunc stores target value in second ident - isIllegal = true; - } - }); - if (!isIllegal) { - //if (dcheck('legalizer')) dprint('no need to legalize \n' + dump(item)); - i++; - continue; - } - // Unfold this line. If we unfolded, we need to return and process the lines we just - // generated - they may need legalization too - var unfolded = []; - walkAndModifyInterdata(item, function(subItem) { - // Unfold all non-value interitems that we can, and also unfold all numbers (doing the latter - // makes it easier later since we can then assume illegal expressions are always variables - // accessible through ident$x, and not constants we need to parse then and there) - if (subItem != item && (!(subItem.intertype in UNUNFOLDABLE) || - (subItem.intertype == 'value' && isNumber(subItem.ident) && isIllegalType(subItem.type)))) { - if (item.intertype == 'phi') { - assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue' || subItem.intertype in PARSABLE_LLVM_FUNCTIONS, 'We can only unfold some expressions in phis'); - // we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi - } else { - var tempIdent = '$$etemp$' + (tempId++); - subItem.assignTo = tempIdent; - unfolded.unshift(subItem); - fixUnfolded(subItem); - return { intertype: 'value', ident: tempIdent, type: subItem.type }; - } - } else if (subItem.intertype == 'switch' && isIllegalType(subItem.type)) { - subItem.switchLabels.forEach(function(switchLabel) { - if (switchLabel.value[0] != '$') { - var tempIdent = '$$etemp$' + (tempId++); - unfolded.unshift({ - assignTo: tempIdent, - intertype: 'value', - ident: switchLabel.value, - type: subItem.type - }); - switchLabel.value = tempIdent; - } - }); - } - }); - if (unfolded.length > 0) { - interpLines(label.lines, i-1, unfolded); - Array.prototype.splice.apply(label.lines, [i, 0].concat(unfolded)); - continue; // remain at this index, to unfold newly generated lines - } - // This is an illegal-containing line, and it is unfolded. Legalize it now - dprint('legalizer', 'Legalizing ' + item.intertype + ' at line ' + item.lineNum); - var finalizer = null; - switch (item.intertype) { - case 'store': { - var toAdd = []; - bits = getBits(item.valueType); - var elements = getLegalParams([item.value], bits)[0]; - var j = 0; - elements.forEach(function(element) { - var tempVar = '$st$' + (tempId++) + '$' + j; - toAdd.push({ - intertype: 'getelementptr', - assignTo: tempVar, - ident: item.pointer.ident, - type: '[0 x i32]*', - params: [ - { intertype: 'value', ident: item.pointer.ident, type: '[0 x i32]*' }, // technically a bitcase is needed in llvm, but not for us - { intertype: 'value', ident: '0', type: 'i32' }, - { intertype: 'value', ident: j.toString(), type: 'i32' } - ], - }); - var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits - toAdd.push({ - intertype: 'store', - valueType: actualSizeType, - value: { intertype: 'value', ident: element.ident, type: actualSizeType }, - pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, - ident: tempVar, - pointerType: actualSizeType + '*', - align: item.align, - }); - j++; - }); - Types.needAnalysis['[0 x i32]'] = 0; - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - // call, return: Return the first 32 bits, the rest are in temp - case 'call': { - var toAdd = [value]; - // legalize parameters - legalizeFunctionParameters(value.params); - // legalize return value, if any - var returnType = getReturnType(item.type); - if (value.assignTo && isIllegalType(returnType)) { - bits = getBits(returnType); - var elements = getLegalVars(item.assignTo, bits); - // legalize return value - value.assignTo = elements[0].ident; - for (var j = 1; j < elements.length; j++) { - var element = elements[j]; - toAdd.push({ - intertype: 'value', - assignTo: element.ident, - type: 'i' + element.bits, - ident: 'tempRet' + (j - 1) - }); - assert(j<10); // TODO: dynamically create more than 10 tempRet-s - } - } - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - case 'landingpad': { - // not much to legalize - i++; - continue; - } - case 'return': { - bits = getBits(item.type); - var elements = getLegalVars(item.value.ident, bits); - item.value.ident = '('; - for (var j = 1; j < elements.length; j++) { - item.value.ident += 'tempRet' + (j-1) + '=' + elements[j].ident + ','; - } - item.value.ident += elements[0].ident + ')'; - i++; - continue; - } - case 'invoke': { - legalizeFunctionParameters(value.params); - // We can't add lines after this, since invoke already modifies control flow. So we handle the return in invoke - i++; - continue; - } - case 'value': { - bits = getBits(value.type); - var elements = getLegalVars(item.assignTo, bits); - var values = getLegalLiterals(item.ident, bits); - var j = 0; - var toAdd = elements.map(function(element) { - return { - intertype: 'value', - assignTo: element.ident, - type: 'i' + bits, - ident: values[j++].ident - }; - }); - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - case 'structvalue': { - bits = getBits(value.type); - var elements = getLegalVars(item.assignTo, bits); - var toAdd = []; - for (var j = 0; j < item.params.length; j++) { - toAdd[j] = { - intertype: 'value', - assignTo: elements[j].ident, - type: 'i32', - ident: item.params[j].ident - }; - } - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - case 'load': { - bits = getBits(value.valueType); - var elements = getLegalVars(item.assignTo, bits); - var j = 0; - var toAdd = []; - elements.forEach(function(element) { - var tempVar = '$ld$' + (tempId++) + '$' + j; - toAdd.push({ - intertype: 'getelementptr', - assignTo: tempVar, - ident: value.pointer.ident, - type: '[0 x i32]*', - params: [ - { intertype: 'value', ident: value.pointer.ident, type: '[0 x i32]*' }, // technically bitcast is needed in llvm, but not for us - { intertype: 'value', ident: '0', type: 'i32' }, - { intertype: 'value', ident: j.toString(), type: 'i32' } - ] - }); - var newItem = { - intertype: 'load', - assignTo: element.ident, - pointerType: 'i32*', - valueType: 'i32', - type: 'i32', - pointer: { intertype: 'value', ident: tempVar, type: 'i32*' }, - ident: tempVar, - align: value.align - }; - var newItem2 = null; - // The last one may be smaller than 32 bits - if (element.bits < 32) { - newItem.assignTo += '$preadd$'; - newItem2 = { - intertype: 'mathop', - op: 'and', - assignTo: element.ident, - type: 'i32', - params: [{ - intertype: 'value', - type: 'i32', - ident: newItem.assignTo - }, { - intertype: 'value', - type: 'i32', - ident: (0xffffffff >>> (32 - element.bits)).toString() - }], - }; - } - toAdd.push(newItem); - if (newItem2) toAdd.push(newItem2); - j++; - }); - Types.needAnalysis['[0 x i32]'] = 0; - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - case 'phi': { - bits = getBits(value.type); - var toAdd = []; - var elements = getLegalVars(item.assignTo, bits); - var j = 0; - var values = getLegalParams(value.params, bits); - elements.forEach(function(element) { - var k = 0; - toAdd.push({ - intertype: 'phi', - assignTo: element.ident, - type: 'i' + element.bits, - params: value.params.map(function(param) { - return { - intertype: 'phiparam', - label: param.label, - value: values[k++][j] - }; - }) - }); - j++; - }); - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - case 'switch': { - i++; - continue; // special case, handled in makeComparison - } - case 'va_arg': { - assert(value.type == 'i64'); - assert(value.value.type == 'i32*', value.value.type); - i += removeAndAdd(label.lines, i, range(2).map(function(x) { - return { - intertype: 'va_arg', - assignTo: value.assignTo + '$' + x, - type: 'i32', - value: { - intertype: 'value', - ident: value.value.ident, // We read twice from the same i32* var, incrementing // + '$' + x, - type: 'i32*' - } - }; - })); - continue; - } - case 'extractvalue': { // XXX we assume 32-bit alignment in extractvalue/insertvalue, - // but in theory they can run on packed structs too (see use getStructuralTypePartBits) - // potentially legalize the actual extracted value too if it is >32 bits, not just the extraction in general - var index = item.indexes[0][0].text; - var parts = getStructureTypeParts(item.type); - var indexedType = parts[index]; - var targetBits = getBits(indexedType); - var sourceBits = getBits(item.type); - var elements = getLegalVars(item.assignTo, targetBits, true); // possibly illegal - var sourceElements = getLegalVars(item.ident, sourceBits); // definitely illegal - var toAdd = []; - var sourceIndex = 0; - for (var partIndex = 0; partIndex < parts.length; partIndex++) { - if (partIndex == index) { - for (var j = 0; j < elements.length; j++) { - toAdd.push({ - intertype: 'value', - assignTo: elements[j].ident, - type: 'i' + elements[j].bits, - ident: sourceElements[sourceIndex+j].ident - }); - } - break; - } - sourceIndex += getStructuralTypePartBits(parts[partIndex])/32; - } - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - case 'insertvalue': { - var index = item.indexes[0][0].text; // the modified index - var parts = getStructureTypeParts(item.type); - var indexedType = parts[index]; - var indexBits = getBits(indexedType); - var bits = getBits(item.type); // source and target - bits = getBits(value.type); - var toAdd = []; - var elements = getLegalVars(item.assignTo, bits); - var sourceElements = getLegalVars(item.ident, bits); - var indexElements = getLegalVars(item.value.ident, indexBits, true); // possibly legal - var sourceIndex = 0; - for (var partIndex = 0; partIndex < parts.length; partIndex++) { - var currNum = getStructuralTypePartBits(parts[partIndex])/32; - for (var j = 0; j < currNum; j++) { - toAdd.push({ - intertype: 'value', - assignTo: elements[sourceIndex+j].ident, - type: 'i' + elements[sourceIndex+j].bits, - ident: partIndex == index ? indexElements[j].ident : sourceElements[sourceIndex+j].ident - }); - } - sourceIndex += currNum; - } - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - case 'bitcast': { - var inType = item.type2; - var outType = item.type; - if ((inType in Compiletime.INT_TYPES && outType in Compiletime.FLOAT_TYPES) || - (inType in Compiletime.FLOAT_TYPES && outType in Compiletime.INT_TYPES)) { - i++; - continue; // special case, handled in processMathop - } - // fall through - } - case 'inttoptr': case 'ptrtoint': case 'zext': case 'sext': case 'trunc': case 'ashr': case 'lshr': case 'shl': case 'or': case 'and': case 'xor': { - value = { - op: item.intertype, - variant: item.variant, - type: item.type, - params: item.params - }; - // fall through - } - case 'mathop': { - var toAdd = []; - var sourceBits = getBits(value.params[0].type); - // All mathops can be parametrized by how many shifts we do, and how big the source is - var shifts = 0; - var targetBits = sourceBits; - var processor = null; - var signed = false; - switch (value.op) { - case 'ashr': { - signed = true; - // fall through - } - case 'lshr': { - shifts = parseInt(value.params[1].ident); - break; - } - case 'shl': { - shifts = -parseInt(value.params[1].ident); - break; - } - case 'sext': { - signed = true; - // fall through - } - case 'trunc': case 'zext': case 'ptrtoint': { - targetBits = getBits(value.params[1] ? value.params[1].ident : value.type); - break; - } - case 'inttoptr': { - targetBits = 32; - break; - } - case 'bitcast': { - if (!sourceBits) { - // we can be asked to bitcast doubles or such to integers, handle that as best we can - sourceBits = targetBits = Runtime.getNativeTypeSize(value.params[0].type); - warn('legalizing non-integer bitcast on ll #' + item.lineNum); - } - break; - } - case 'select': { - sourceBits = targetBits = getBits(value.params[1].type); - var params = getLegalParams(value.params.slice(1), sourceBits); - processor = function(result, j) { - return { - intertype: 'mathop', - op: 'select', - type: 'i' + params[0][j].bits, - params: [ - value.params[0], - { intertype: 'value', ident: params[0][j].ident, type: 'i' + params[0][j].bits }, - { intertype: 'value', ident: params[1][j].ident, type: 'i' + params[1][j].bits } - ] - }; - }; - break; - } - case 'or': case 'and': case 'xor': case 'icmp': { - var otherElements = getLegalVars(value.params[1].ident, sourceBits); - processor = function(result, j) { - return { - intertype: 'mathop', - op: value.op, - variant: value.variant, - type: 'i' + otherElements[j].bits, - params: [ - result, - { intertype: 'value', ident: otherElements[j].ident, type: 'i' + otherElements[j].bits } - ] - }; - }; - if (value.op == 'icmp') { - if (sourceBits == 64) { // handle the i64 case in processMathOp, where we handle full i64 math - i++; - continue; - } - finalizer = function() { - var ident = ''; - for (var i = 0; i < targetElements.length; i++) { - if (i > 0) { - switch(value.variant) { - case 'eq': ident += '&'; break; - case 'ne': ident += '|'; break; - default: throw 'unhandleable illegal icmp: ' + value.variant; - } - } - ident += targetElements[i].ident; - } - return { - intertype: 'value', - ident: ident, - type: 'rawJS', - assignTo: item.assignTo - }; - } - } - break; - } - case 'add': case 'sub': case 'sdiv': case 'udiv': case 'mul': case 'urem': case 'srem': { - if (sourceBits < 32) { - // when we add illegal types like i24, we must work on the singleton chunks - item.assignTo += '$0'; - item.params[0].ident += '$0'; - item.params[1].ident += '$0'; - } - // fall through - } - case 'uitofp': case 'sitofp': case 'fptosi': case 'fptoui': { - // We cannot do these in parallel chunks of 32-bit operations. We will handle these in processMathop - i++; - continue; - } - default: throw 'Invalid mathop for legalization: ' + [value.op, item.lineNum, dump(item)]; - } - // Do the legalization - var sourceElements = getLegalVars(value.params[0].ident, sourceBits, true); - if (!isNumber(shifts)) { - // We can't statically legalize this, do the operation at runtime TODO: optimize - assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits'); - assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts'); - Types.preciseI64MathUsed = 1; - value.intertype = 'value'; - value.ident = makeVarDef(value.assignTo) + '$0=' + - asmCoercion('_bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' + - asmCoercion(sourceElements[0].ident, 'i32') + ',' + - asmCoercion(sourceElements[1].ident, 'i32') + ',' + - asmCoercion(value.params[1].ident + '$0', 'i32') + ')', 'i32' - ) + ';' + - makeVarDef(value.assignTo) + '$1=tempRet0;'; - value.vars = [[value.assignTo + '$0', 'i32'], [value.assignTo + '$1', 'i32']]; - value.assignTo = null; - i++; - continue; - } - var targetElements = getLegalVars(item.assignTo, targetBits); - var sign = shifts >= 0 ? 1 : -1; - var shiftOp = shifts >= 0 ? 'shl' : 'lshr'; - var shiftOpReverse = shifts >= 0 ? 'lshr' : 'shl'; - var whole = (shifts/32)|0; // Remove fractional part either for positive or negative number. - var fraction = Math.abs(shifts % 32); - if (signed) { - var signedFill = { - intertype: 'mathop', - op: 'select', - variant: 's', - type: 'i32', - params: [{ - intertype: 'mathop', - op: 'icmp', - variant: 'slt', - type: 'i32', - params: [ - { intertype: 'value', ident: sourceElements[sourceElements.length-1].ident, type: 'i' + Math.min(sourceBits, 32) }, - { intertype: 'value', ident: '0', type: 'i32' } - ] - }, - { intertype: 'value', ident: '-1', type: 'i32' }, - { intertype: 'value', ident: '0', type: 'i32' }, - ] - }; - } - for (var j = 0; j < targetElements.length; j++) { - var inBounds = j + whole >= 0 && j + whole < sourceElements.length; - var result; - if (inBounds || !signed) { - result = { - intertype: 'value', - ident: inBounds ? sourceElements[j + whole].ident : '0', - type: 'i' + Math.min(sourceBits, 32), - }; - if (j == 0 && sourceBits < 32) { - // zext sign correction - var result2 = { - intertype: 'mathop', - op: isUnsignedOp(value.op) ? 'zext' : 'sext', - params: [result, { - intertype: 'type', - ident: 'i32', - type: 'i' + sourceBits - }], - type: 'i32' - }; - result = result2; - } - } else { - // out of bounds and signed - result = copy(signedFill); - } - if (fraction != 0) { - var other; - var otherInBounds = j + sign + whole >= 0 && j + sign + whole < sourceElements.length; - if (otherInBounds || !signed) { - other = { - intertype: 'value', - ident: otherInBounds ? sourceElements[j + sign + whole].ident : '0', - type: 'i32', - }; - } else { - other = copy(signedFill); - } - other = { - intertype: 'mathop', - op: shiftOp, - type: 'i32', - params: [ - other, - { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' } - ] - }; - result = { - intertype: 'mathop', - // shifting in 1s from the top is a special case - op: (signed && shifts >= 0 && j + sign + whole >= sourceElements.length) ? 'ashr' : shiftOpReverse, - type: 'i32', - params: [ - result, - { intertype: 'value', ident: fraction.toString(), type: 'i32' } - ] - }; - result = { - intertype: 'mathop', - op: 'or', - type: 'i32', - params: [ - result, - other - ] - } - } - if (targetElements[j].bits < 32 && shifts < 0) { - // truncate bits that fall off the end. This is not needed in most cases, can probably be optimized out - result = { - intertype: 'mathop', - op: 'and', - type: 'i32', - params: [ - result, - { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' } - ] - } - } - if (processor) { - result = processor(result, j); - } - result.assignTo = targetElements[j].ident; - toAdd.push(result); - } - if (targetBits <= 32) { - // We are generating a normal legal type here - legalValue = { intertype: 'value', ident: targetElements[0].ident, type: 'i32' }; - if (targetBits < 32) { - legalValue = { - intertype: 'mathop', - op: 'and', - type: 'i32', - params: [ - legalValue, - { intertype: 'value', ident: (Math.pow(2, targetBits)-1).toString(), type: 'i32' } - ] - } - }; - legalValue.assignTo = item.assignTo; - toAdd.push(legalValue); - } else if (finalizer) { - toAdd.push(finalizer()); - } - i += removeAndAdd(label.lines, i, toAdd); - continue; - } - } - assert(0, 'Could not legalize illegal line: ' + [item.lineNum, dump(item)]); - } - if (dcheck('legalizer')) dprint('zz legalized: \n' + dump(label.lines)); - }); - }); - - // Add function lines to func.lines, after our modifications to the label lines - data.functions.forEach(function(func) { - func.labels.forEach(function(label) { - func.lines = func.lines.concat(label.lines); - }); - }); - } - - function addTypeInternal(type) { - if (type.length == 1) return; - if (Types.types[type]) return; - if (['internal', 'hidden', 'inbounds', 'void'].indexOf(type) != -1) return; - if (Compiletime.isNumberType(type)) return; - dprint('types', 'Adding type: ' + type); - - // 'blocks': [14 x %struct.X] etc. If this is a pointer, we need - // to look at the underlying type - it was not defined explicitly - // anywhere else. - var nonPointing = removeAllPointing(type); - if (Types.types[nonPointing]) return; - var check = /^\[(\d+)\ x\ (.*)\]$/.exec(nonPointing); - if (check) { - var num = parseInt(check[1]); - num = Math.max(num, 1); // [0 x something] is used not for allocations and such of course, but - // for indexing - for an |array of unknown length|, basically. So we - // define the 'type' as having a single field. TODO: Ensure as a sanity - // check that we never allocate with this (either as a child structure - // in the analyzer, or in calcSize in alloca). - var subType = check[2]; - addTypeInternal(subType); // needed for anonymous structure definitions (see below) - - var fields = [subType, subType]; // Two, so we get the flatFactor right. We care about the flatFactor, not the size here. see calculateStructAlignment - Types.types[nonPointing] = { - name_: nonPointing, - fields: fields, - lineNum: '?' - }; - newTypes[nonPointing] = 1; - // Also add a |[0 x type]| type - var zerod = '[0 x ' + subType + ']'; - if (!Types.types[zerod]) { - Types.types[zerod] = { - name_: zerod, - fields: fields, - lineNum: '?' - }; - newTypes[zerod] = 1; - } - return; - } - - // anonymous structure definition, for example |{ i32, i8*, void ()*, i32 }| - if (type[0] == '{' || type[0] == '<') { - type = nonPointing; - var packed = type[0] == '<'; - var internal = type; - if (packed) { - if (type[1] !== '{') { - // vector type, <4 x float> etc. - var size = getVectorSize(type); - Types.types[type] = { - name_: type, - fields: zeros(size).map(function() { - return getVectorNativeType(type); - }), - packed: false, - flatSize: 4*size, - lineNum: '?' - }; - return; - } - if (internal[internal.length-1] != '>') { - warnOnce('ignoring type ' + internal); - return; // function pointer or such - } - internal = internal.substr(1, internal.length-2); - } - assert(internal[0] == '{', internal); - if (internal[internal.length-1] != '}') { - warnOnce('ignoring type ' + internal); - return; // function pointer or such - } - internal = internal.substr(2, internal.length-4); - Types.types[type] = { - name_: type, - fields: splitTokenList(tokenize(internal)).map(function(segment) { - return segment[0].text; - }), - packed: packed, - lineNum: '?' - }; - newTypes[type] = 1; - return; - } - - if (isPointerType(type)) return; - if (['['].indexOf(type) != -1) return; - Types.types[type] = { - name_: type, - fields: [ 'i' + (QUANTUM_SIZE*8) ], // a single quantum size - flatSize: 1, - lineNum: '?' - }; - newTypes[type] = 1; - } - - function addType(type) { - addTypeInternal(type); - if (QUANTUM_SIZE === 1) { - Types.flipTypes(); - addTypeInternal(type); - Types.flipTypes(); - } - } - - // Typevestigator - function typevestigator() { - if (sidePass) { // Do not investigate in the main pass - it is only valid to start to do so in the first side pass, - // which handles type definitions, and later. Doing so before the first side pass will result in - // making bad guesses about types which are actually defined - for (var type in Types.needAnalysis) { - if (type) addType(type); - } - Types.needAnalysis = {}; - } - } - - // Type analyzer - function analyzeTypes(fatTypes) { - var types = Types.types; - - // 'fields' is the raw list of LLVM fields. However, we embed - // child structures into parent structures, basically like C. - // So { int, { int, int }, int } would be represented as - // an Array of 4 ints. getelementptr on the parent would take - // values 0, 1, 2, where 2 is the entire middle structure. - // We also need to be careful with getelementptr to child - // structures - we return a pointer to the same slab, just - // a different offset. Likewise, need to be careful for - // getelementptr of 2 (the last int) - it's real index is 4. - // The benefit of this approach is inheritance - - // { { ancestor } , etc. } = descendant - // In this case it is easy to bitcast ancestor to descendant - // pointers - nothing needs to be done. If the ancestor were - // a new slab, it would need some pointer to the outer one - // for casting in that direction. - // TODO: bitcasts of non-inheritance cases of embedding (not at start) - var more = true; - while (more) { - more = false; - for (var typeName in newTypes) { - var type = types[typeName]; - if (type.flatIndexes) continue; - var ready = true; - type.fields.forEach(function(field) { - if (isStructType(field)) { - if (!types[field]) { - addType(field); - ready = false; - } else { - if (!types[field].flatIndexes) { - newTypes[field] = 1; - ready = false; - } - } - } - }); - if (!ready) { - more = true; - continue; - } - - Runtime.calculateStructAlignment(type); - - if (dcheck('types')) dprint('type (fat=' + !!fatTypes + '): ' + type.name_ + ' : ' + JSON.stringify(type.fields)); - if (dcheck('types')) dprint(' has final size of ' + type.flatSize + ', flatting: ' + type.needsFlattening + ' ? ' + (type.flatFactor ? type.flatFactor : JSON.stringify(type.flatIndexes))); - } - } - - if (QUANTUM_SIZE === 1 && !fatTypes) { - Types.flipTypes(); - // Fake a quantum size of 4 for fat types. TODO: Might want non-4 for some reason? - var trueQuantumSize = QUANTUM_SIZE; - Runtime.QUANTUM_SIZE = 4; - analyzeTypes(true); - Runtime.QUANTUM_SIZE = trueQuantumSize; - Types.flipTypes(); - } - - newTypes = null; - } - - // Variable analyzer - function variableAnalyzer() { - // Globals - - var old = item.globalVariables; - item.globalVariables = {}; - old.forEach(function(variable) { - variable.impl = 'emulated'; // All global variables are emulated, for now. Consider optimizing later if useful - item.globalVariables[variable.ident] = variable; - }); - - // Function locals - - item.functions.forEach(function(func) { - func.variables = {}; - - // LLVM is SSA, so we always have a single assignment/write. We care about - // the reads/other uses. - - // Function parameters - func.params.forEach(function(param) { - if (param.intertype !== 'varargs') { - if (func.variables[param.ident]) warn('cannot have duplicate variable names: ' + param.ident); // toNiceIdent collisions? - func.variables[param.ident] = { - ident: param.ident, - type: param.type, - origin: 'funcparam', - lineNum: func.lineNum, - rawLinesIndex: -1 - }; - } - }); - - // Normal variables - func.lines.forEach(function(item, i) { - if (item.assignTo) { - if (func.variables[item.assignTo]) warn('cannot have duplicate variable names: ' + item.assignTo); // toNiceIdent collisions? - var variable = func.variables[item.assignTo] = { - ident: item.assignTo, - type: item.type, - origin: item.intertype, - lineNum: item.lineNum, - rawLinesIndex: i - }; - if (variable.origin === 'alloca') { - variable.allocatedNum = item.ident; - } - if (variable.origin === 'call') { - variable.type = getReturnType(variable.type); - } - } - }); - - if (QUANTUM_SIZE === 1) { - // Second pass over variables - notice when types are crossed by bitcast - - func.lines.forEach(function(item) { - if (item.assignTo && item.intertype === 'bitcast') { - // bitcasts are unique in that they convert one pointer to another. We - // sometimes need to know the original type of a pointer, so we save that. - // - // originalType is the type this variable is created from - // derivedTypes are the types that this variable is cast into - func.variables[item.assignTo].originalType = item.type2; - - if (!isNumber(item.assignTo)) { - if (!func.variables[item.assignTo].derivedTypes) { - func.variables[item.assignTo].derivedTypes = []; - } - func.variables[item.assignTo].derivedTypes.push(item.type); - } - } - }); - } - - // Analyze variable uses - - function analyzeVariableUses() { - dprint('vars', 'Analyzing variables for ' + func.ident + '\n'); - - for (vname in func.variables) { - var variable = func.variables[vname]; - - // Whether the value itself is used. For an int, always yes. For a pointer, - // we might never use the pointer's value - we might always just store to it / - // read from it. If so, then we can optimize away the pointer. - variable.hasValueTaken = false; - - variable.pointingLevels = pointingLevels(variable.type); - - variable.uses = 0; - } - - // TODO: improve the analysis precision. bitcast, for example, means we take the value, but perhaps we only use it to load/store - var inNoop = 0; - func.lines.forEach(function(line) { - walkInterdata(line, function(item) { - if (item.intertype == 'noop') inNoop++; - if (!inNoop) { - if (item.ident in func.variables) { - func.variables[item.ident].uses++; - - if (item.intertype != 'load' && item.intertype != 'store') { - func.variables[item.ident].hasValueTaken = true; - } - } - } - }, function(item) { - if (item.intertype == 'noop') inNoop--; - }); - }); - - //if (dcheck('vars')) dprint('analyzed variables: ' + dump(func.variables)); - } - - analyzeVariableUses(); - - // Decision time - - for (vname in func.variables) { - var variable = func.variables[vname]; - var pointedType = pointingLevels(variable.type) > 0 ? removePointing(variable.type) : null; - if (variable.origin == 'getelementptr') { - // Use our implementation that emulates pointers etc. - // TODO Can we perhaps nativize some of these? However to do so, we need to discover their - // true types; we have '?' for them now, as they cannot be discovered in the intertyper. - variable.impl = VAR_EMULATED; - } else if (variable.origin == 'funcparam') { - variable.impl = VAR_EMULATED; - } else if (variable.type == 'i64*') { - variable.impl = VAR_EMULATED; - } else if (MICRO_OPTS && variable.pointingLevels === 0) { - // A simple int value, can be implemented as a native variable - variable.impl = VAR_NATIVE; - } else if (MICRO_OPTS && variable.origin === 'alloca' && !variable.hasValueTaken && - variable.allocatedNum === 1 && - (Compiletime.isNumberType(pointedType) || isPointerType(pointedType))) { - // A pointer to a value which is only accessible through this pointer. Basically - // a local value on the stack, which nothing fancy is done on. So we can - // optimize away the pointing altogether, and just have a native variable - variable.impl = VAR_NATIVIZED; - } else { - variable.impl = VAR_EMULATED; - } - if (dcheck('vars')) dprint('// var ' + vname + ': ' + JSON.stringify(variable)); - } - }); - } - - // Sign analyzer - // - // Analyze our variables and detect their signs. - // We can read signed or unsigned values and prevent the need for signing - // corrections. If on the other hand we are doing corrections anyhow, then - // we can skip this pass. - // - // For each variable that is the result of a Load, we look a little forward - // to see where it is used. We only care about mathops, since only they - // need signs. - // - function signalyzer() { - if (CORRECT_SIGNS == 1) return; - - function seekIdent(item, obj) { - if (item.ident === obj.ident) { - obj.found++; - } - } - - function seekMathop(item, obj) { - if (item.intertype === 'mathop' && obj.found && !obj.decided) { - if (isUnsignedOp(item.op, item.variant)) { - obj.unsigned++; - } else { - obj.signed++; - } - } - } - - item.functions.forEach(function(func) { - func.lines.forEach(function(line, i) { - if (line.intertype === 'load') { - // Floats have no concept of signedness. Mark them as 'signed', which is the default, for which we do nothing - if (line.type in Compiletime.FLOAT_TYPES) { - line.unsigned = false; - return; - } - // Booleans are always unsigned - var data = func.variables[line.assignTo]; - if (data.type === 'i1') { - line.unsigned = true; - return; - } - - var total = data.uses; - if (total === 0) return; - var obj = { ident: line.assignTo, found: 0, unsigned: 0, signed: 0, total: total }; - // in loops with phis, we can also be used *before* we are defined - var j = i-1, k = i+1; - while(1) { - assert(j >= 0 || k < func.lines.length, 'Signalyzer ran out of space to look for sign indications for line ' + line.lineNum); - if (j >= 0 && walkInterdata(func.lines[j], seekIdent, seekMathop, obj)) break; - if (k < func.lines.length && walkInterdata(func.lines[k], seekIdent, seekMathop, obj)) break; - if (obj.total && obj.found >= obj.total) break; // see comment below - j -= 1; - k += 1; - } - - // unsigned+signed might be < total, since the same ident can appear multiple times in the same mathop. - // found can actually be > total, since we currently have the same ident in a GEP (see cubescript test) - // in the GEP item, and a child item (we have the ident copied onto the GEP item as a convenience). - // probably not a bug-causer, but FIXME. see also a reference to this above - // we also leave the loop above potentially early due to this. otherwise, though, we end up scanning the - // entire function in some cases which is very slow - assert(obj.found >= obj.total, 'Could not Signalyze line ' + line.lineNum); - line.unsigned = obj.unsigned > 0; - dprint('vars', 'Signalyzer: ' + line.assignTo + ' has unsigned == ' + line.unsigned + ' (line ' + line.lineNum + ')'); - } - }); - }); - } - - // Quantum fixer - // - // See settings.js for the meaning of QUANTUM_SIZE. The issue we fix here is, - // to correct the .ll assembly code so that things work with QUANTUM_SIZE=1. - // - function quantumFixer() { - if (QUANTUM_SIZE !== 1) return; - - // ptrs: the indexes of parameters that are pointers, whose originalType is what we want - // bytes: the index of the 'bytes' parameter - // TODO: malloc, realloc? - var FIXABLE_CALLS = { - 'memcpy': { ptrs: [0,1], bytes: 2 }, - 'memmove': { ptrs: [0,1], bytes: 2 }, - 'memset': { ptrs: [0], bytes: 2 }, - 'qsort': { ptrs: [0], bytes: 2 } - }; - - function getSize(types, type, fat) { - if (types[type]) return types[type].flatSize; - if (fat) { - Runtime.QUANTUM_SIZE = 4; - } - var ret = Runtime.getNativeTypeSize(type); - if (fat) { - Runtime.QUANTUM_SIZE = 1; - } - return ret; - } - - function getFlatIndexes(types, type) { - if (types[type]) return types[type].flatIndexes; - return [0]; - } - - item.functions.forEach(function(func) { - function getOriginalType(param) { - function get() { - if (param.intertype === 'value' && !isNumber(param.ident)) { - if (func.variables[param.ident]) { - return func.variables[param.ident].originalType || null; - } else { - return item.globalVariables[param.ident].originalType; - } - } else if (param.intertype === 'bitcast') { - return param.params[0].type; - } else if (param.intertype === 'getelementptr') { - if (param.params[0].type[0] === '[') return param.params[0].type; - } - return null; - } - var ret = get(); - if (ret && ret[0] === '[') { - var check = /^\[(\d+)\ x\ (.*)\]\*$/.exec(ret); - assert(check); - ret = check[2] + '*'; - } - return ret; - } - - func.lines.forEach(function(line) { - // Call - if (line.intertype === 'call') { - var funcIdent = LibraryManager.getRootIdent(line.ident.substr(1)); - var fixData = FIXABLE_CALLS[funcIdent]; - if (!fixData) return; - var ptrs = fixData.ptrs.map(function(ptr) { return line.params[ptr] }); - var bytes = line.params[fixData.bytes].ident; - - // Only consider original types. This assumes memcpy always has pointers bitcast to i8* - var originalTypes = ptrs.map(getOriginalType); - for (var i = 0; i < originalTypes.length; i++) { - if (!originalTypes[i]) return; - } - originalTypes = originalTypes.map(function(type) { return removePointing(type) }); - var sizes = originalTypes.map(function(type) { return getSize(Types.types, type) }); - var fatSizes = originalTypes.map(function(type) { return getSize(Types.fatTypes, type, true) }); - // The sizes may not be identical, if we copy a descendant class into a parent class. We use - // the smaller size in that case. However, this may also be a bug, it is hard to tell, hence a warning - warn(dedup(sizes).length === 1, 'All sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' + - line.lineNum); - warn(dedup(fatSizes).length === 1, 'All fat sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' + - line.lineNum); - var size = Math.min.apply(null, sizes); - var fatSize = Math.min.apply(null, fatSizes); - if (isNumber(bytes)) { - // Figure out how much to copy. - var fixedBytes; - if (bytes % fatSize === 0) { - fixedBytes = size*(bytes/fatSize); - } else if (fatSize % bytes === 0 && size % (fatSize/bytes) === 0) { - // Assume this is a simple array. XXX We can be wrong though! See next TODO - fixedBytes = size/(fatSize/bytes); - } else { - // Just part of a structure. Align them to see how many fields. Err on copying more. - // TODO: properly generate a complete structure, including nesteds, and calculate on that - var flatIndexes = getFlatIndexes(Types.types, originalTypes[0]).concat(size); - var fatFlatIndexes = getFlatIndexes(Types.fatTypes, originalTypes[0]).concat(fatSize); - var index = 0; - var left = bytes; - fixedBytes = 0; - while (left > 0) { - left -= fatFlatIndexes[index+1] - fatFlatIndexes[index]; // note: we copy the alignment bytes too, which is unneeded - fixedBytes += flatIndexes[index+1] - flatIndexes[index]; - } - } - line.params[fixData.bytes].ident = fixedBytes; - } else { - line.params[fixData.bytes].intertype = 'jsvalue'; - // We have an assertion in library::memcpy() that this is round - line.params[fixData.bytes].ident = size + '*(' + bytes + '/' + fatSize + ')'; - } - } - }); - }); - - // 2nd part - fix hardcoded constant offsets in global constants - values(item.globalVariables).forEach(function(variable) { - function recurse(item) { - if (item.contents) { - item.contents.forEach(recurse); - } else if (item.intertype === 'getelementptr' && item.params[0].intertype === 'bitcast' && item.params[0].type === 'i8*') { - var originalType = removePointing(item.params[0].params[0].type); - var fatSize = getSize(Types.fatTypes, originalType, true); - var slimSize = getSize(Types.types, originalType, false); - assert(fatSize % slimSize === 0); - item.params.slice(1).forEach(function(param) { - if (param.intertype === 'value' && isNumber(param.ident)) { - var corrected = parseInt(param.ident)/(fatSize/slimSize); - assert(corrected % 1 === 0); - param.ident = corrected.toString(); - } - }); - } else if (item.params) { - item.params.forEach(recurse); - } - } - if (!variable.external && variable.value) recurse(variable.value); - }); - } - - function operateOnLabels(line, func) { - function process(item, id) { - ['label', 'labelTrue', 'labelFalse', 'toLabel', 'unwindLabel', 'defaultLabel'].forEach(function(id) { - if (item[id]) { - func(item, id); - } - }); - } - if (line.intertype in BRANCH_INVOKE) { - process(line); - } else if (line.intertype == 'switch') { - process(line); - line.switchLabels.forEach(process); - } - } - - // Label analyzer - function labelAnalyzer() { - item.functions.forEach(function(func) { - func.labelsDict = {}; - func.labelIds = {}; - func.labelIdsInverse = {}; - func.labelIdCounter = 1; - func.labels.forEach(function(label) { - if (!(label.ident in func.labelIds)) { - func.labelIds[label.ident] = func.labelIdCounter++; - func.labelIdsInverse[func.labelIdCounter-1] = label.ident; - } - }); - var entryIdent = func.labels[0].ident; - - // Minify label ids to numeric ids. - func.labels.forEach(function(label) { - label.ident = func.labelIds[label.ident]; - label.lines.forEach(function(line) { - operateOnLabels(line, function(item, id) { - item[id] = func.labelIds[item[id]].toString(); // strings, because we will append as we process - }); - }); - }); - - func.labels.forEach(function(label) { - func.labelsDict[label.ident] = label; - }); - - // Correct phis - func.labels.forEach(function(label) { - label.lines.forEach(function(phi) { - if (phi.intertype == 'phi') { - for (var i = 0; i < phi.params.length; i++) { - phi.params[i].label = func.labelIds[phi.params[i].label]; - if (VERBOSE && !phi.params[i].label) warn('phi refers to nonexistent label on line ' + phi.lineNum); - } - } - }); - }); - - func.lines.forEach(function(line) { - if (line.intertype == 'indirectbr') { - func.forceEmulated = true; - } - }); - - function getActualLabelId(labelId) { - if (func.labelsDict[labelId]) return labelId; - // If not present, it must be a surprisingly-named entry (or undefined behavior, in which case, still ok to use the entry) - labelId = func.labelIds[entryIdent]; - assert(func.labelsDict[labelId]); - return labelId; - } - - // Basic longjmp support, see library.js setjmp/longjmp - var setjmp = toNiceIdent('@setjmp'); - func.setjmpTable = null; - for (var i = 0; i < func.labels.length; i++) { - var label = func.labels[i]; - for (var j = 0; j < label.lines.length; j++) { - var line = label.lines[j]; - if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) { - if (line.intertype == 'invoke') { - // setjmp cannot trigger unwinding, so just reduce the invoke to a call + branch - line.intertype = 'call'; - label.lines.push({ - intertype: 'branch', - label: line.toLabel, - lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this - }); - line.toLabel = line.unwindLabel = -2; - } - // split this label into up to the setjmp (including), then a new label for the rest. longjmp will reach the rest - var oldLabel = label.ident; - var newLabel = func.labelIdCounter++; - if (!func.setjmpTable) func.setjmpTable = []; - func.setjmpTable.push({ oldLabel: oldLabel, newLabel: newLabel, assignTo: line.assignTo }); - func.labels.splice(i+1, 0, { - intertype: 'label', - ident: newLabel, - lineNum: label.lineNum + 0.5, - lines: label.lines.slice(j+1) - }); - func.labelsDict[newLabel] = func.labels[i+1]; - label.lines = label.lines.slice(0, j+1); - label.lines.push({ - intertype: 'branch', - label: toNiceIdent(newLabel), - lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this - }); - // Correct phis - func.labels.forEach(function(label) { - label.lines.forEach(function(phi) { - if (phi.intertype == 'phi') { - for (var i = 0; i < phi.params.length; i++) { - var sourceLabelId = getActualLabelId(phi.params[i].label); - if (sourceLabelId == oldLabel) { - phi.params[i].label = newLabel; - } - } - } - }); - }); - } - } - } - if (func.setjmpTable) { - func.forceEmulated = true; - recomputeLines(func); - } - - // Properly implement phis, by pushing them back into the branch - // that leads to here. We will only have the |var| definition in this location. - - // First, push phis back - func.labels.forEach(function(label) { - label.lines.forEach(function(phi) { - if (phi.intertype == 'phi') { - for (var i = 0; i < phi.params.length; i++) { - var param = phi.params[i]; - if (VERBOSE && !param.label) warn('phi refers to nonexistent label on line ' + phi.lineNum); - var sourceLabelId = getActualLabelId(param.label); - if (sourceLabelId) { - var sourceLabel = func.labelsDict[sourceLabelId]; - var lastLine = sourceLabel.lines.slice(-1)[0]; - assert(lastLine.intertype in LLVM.PHI_REACHERS, 'Only some can lead to labels with phis:' + [func.ident, label.ident, lastLine.intertype]); - if (!lastLine.phi) { - lastLine.phi = true; - assert(!lastLine.dependent); - lastLine.dependent = { - intertype: 'phiassigns', - params: [] - }; - }; - lastLine.dependent.params.push({ - intertype: 'phiassign', - ident: phi.assignTo, - value: param.value, - targetLabel: label.ident - }); - } - } - // The assign to phi is now just a var - phi.intertype = 'var'; - phi.ident = phi.assignTo; - phi.assignTo = null; - } - }); - }); - - if (func.ident in NECESSARY_BLOCKADDRS) { - Functions.blockAddresses[func.ident] = {}; - for (var needed in NECESSARY_BLOCKADDRS[func.ident]) { - assert(needed in func.labelIds); - Functions.blockAddresses[func.ident][needed] = func.labelIds[needed]; - } - } - }); - } - - // Stack analyzer - calculate the base stack usage - function stackAnalyzer() { - data.functions.forEach(function(func) { - var lines = func.labels[0].lines; - var hasAlloca = false; - for (var i = 0; i < lines.length; i++) { - var item = lines[i]; - if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident)) break; - item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ? - calcAllocatedSize(item.allocatedType)*item.ident: 0; - hasAlloca = true; - // We need to keep the stack aligned - item.allocatedSize = RuntimeGenerator.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN); - } - var index = 0; - for (var i = 0; i < lines.length; i++) { - var item = lines[i]; - if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident)) break; - item.allocatedIndex = index; - index += item.allocatedSize; - delete item.allocatedSize; - } - func.initialStack = index; - func.otherStackAllocations = false; - if (func.initialStack === 0 && hasAlloca) func.otherStackAllocations = true; // a single alloca of zero still requires us to emit stack support code - while (func.initialStack == 0) { // one-time loop with possible abort in the middle - // If there is no obvious need for stack management, perhaps we don't need it - // (we try to optimize that way with SKIP_STACK_IN_SMALL). However, - // we need to note if stack allocations other than initial allocs can happen here - // If so, we need to rewind the stack when we leave. - - // By-value params are causes of additional allocas (although we could in theory make them normal allocas too) - func.params.forEach(function(param) { - if (param.byVal) { - func.otherStackAllocations = true; - } - }); - if (func.otherStackAllocations) break; - - // Allocas - var finishedInitial = false; - - lines = func.lines; // We need to consider all the function lines now, not just the first label - - for (var i = 0; i < lines.length; i++) { - var item = lines[i]; - if (!finishedInitial && (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident))) { - finishedInitial = true; - } - if (item.intertype == 'alloca' && finishedInitial) { - func.otherStackAllocations = true; - break; - } - } - if (func.otherStackAllocations) break; - - // Varargs - for (var i = 0; i < lines.length; i++) { - var item = lines[i]; - if (item.intertype == 'call' && isVarArgsFunctionType(item.type)) { - func.otherStackAllocations = true; - break; - } - } - if (func.otherStackAllocations) break; - - break; - } - }); - } - - // ReLooper - reconstruct nice loops, as much as possible - // This is now done in the jsify stage, using compiled relooper2 - function relooper() { - function makeBlock(labels, entries, labelsDict, forceEmulated) { - if (labels.length == 0) return null; - dprint('relooping', 'prelooping: ' + entries + ',' + labels.length + ' labels'); - assert(entries && entries[0]); // need at least 1 entry - - var emulated = { - type: 'emulated', - id: 'B', - labels: labels, - entries: entries.slice(0) - }; - return emulated; - } - item.functions.forEach(function(func) { - dprint('relooping', "// relooping function: " + func.ident); - func.block = makeBlock(func.labels, [func.labels[0].ident], func.labelsDict, func.forceEmulated); - }); - } - - // main - castAway(); - legalizer(); - typevestigator(); - analyzeTypes(); - variableAnalyzer(); - signalyzer(); - quantumFixer(); - labelAnalyzer(); - stackAnalyzer(); - relooper(); - - //B.stop('analyzer'); - return item; -} diff --git a/src/compiler.js b/src/compiler.js index b942cd95c7d8b..7077f888250b5 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -232,19 +232,7 @@ if (!BOOTSTRAPPING_STRUCT_INFO) { load('modules.js'); load('parseTools.js'); -load('intertyper.js'); -load('analyzer.js'); load('jsifier.js'); -if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase - RelooperModule = { TOTAL_MEMORY: ceilPowerOfTwo(2*RELOOPER_BUFFER_SIZE) }; - //try { - load(RELOOPER); - //} catch(e) { - // printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir'); - // load('relooper.js'); - //} - assert(typeof Relooper != 'undefined'); -} globalEval(processMacros(preprocess(read('runtime.js')))); Runtime.QUANTUM_SIZE = QUANTUM_SIZE; @@ -261,44 +249,17 @@ NECESSARY_BLOCKADDRS = temp; // Main //=============================== -// Read llvm - -function compile(raw) { - if (raw.search('\r\n') >= 0) { - raw = raw.replace(/\r\n/g, '\n'); // fix windows line endings - } - var lines = raw.split('\n'); - raw = null; - - // Pre-process the LLVM assembly - - Debugging.handleMetadata(lines); - - function runPhase(currPhase) { - //printErr('// JS compiler in action, phase ' + currPhase + typeof lines + (lines === null)); - phase = currPhase; - if (phase != 'pre' && phase != 'glue') { - if (singlePhase) PassManager.load(read(forwardedDataFile)); - - if (phase == 'funcs') { - PreProcessor.eliminateUnneededIntrinsics(lines); - } - } - - // Do it +B = new Benchmarker(); - var intertyped = intertyper(lines); - if (singlePhase) lines = null; - var analyzed = analyzer(intertyped); - intertyped = null; - JSify(analyzed); +try { + if (ll_file) { + var dummyData = {globalVariables: {}, functionStubs: []} + JSify(dummyData); //dumpInterProf(); //printErr(phase + ' paths (fast, slow): ' + [fastPaths, slowPaths]); B.print(phase); - phase = null; - if (DEBUG_MEMORY) { print('zzz. last gc: ' + gc()); MemoryDebugger.dump(); @@ -306,31 +267,6 @@ function compile(raw) { while(1){}; } } - - // Normal operation is for each execution of compiler.js to run a single phase. The calling script sends us exactly the information we need, and it is easy to parallelize operation that way. However, it is also possible to run in an unoptimal multiphase mode, where a single invocation goes from ll to js directly. This is not recommended and will likely do a lot of duplicate processing. - singlePhase = !!phase; - - if (singlePhase) { - runPhase(phase); - } else { - runPhase('pre'); - runPhase('funcs'); - runPhase('post'); - } -} - -B = new Benchmarker(); - -try { - if (ll_file) { - if (phase === 'glue') { - compile(';'); - } else if (ll_file.indexOf(String.fromCharCode(10)) == -1) { - compile(read(ll_file)); - } else { - compile(ll_file); // we are given raw .ll - } - } } catch(err) { if (err.toString().indexOf('Aborting compilation due to previous errors') != -1) { // Compiler failed on user error, print out the error message. diff --git a/src/intertyper.js b/src/intertyper.js deleted file mode 100644 index a1e65757b7467..0000000000000 --- a/src/intertyper.js +++ /dev/null @@ -1,1226 +0,0 @@ -//"use strict"; - -// LLVM assembly => internal intermediate representation, which is ready -// to be processed by the later stages. - -var fastPaths = 0, slowPaths = 0; - -var tokenCache = {}; -[',', '{', '}', 'i32', 'label', ';', '4', '0', '1', '2', '255', 'align', 'i8*', 'i8', 'i16', 'getelementptr', 'inbounds', 'unnamed_addr', 'x', 'load', 'preds', 'br', 'i32*', 'i1', 'store', '