From fdf24b478e1b26c0362a1627aca49ef82fd53f6a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Jan 2015 10:15:42 -0800 Subject: [PATCH 01/76] 1.29.0 --- emscripten-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emscripten-version.txt b/emscripten-version.txt index a5096ba657ec9..5ff413d623980 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.28.3 +1.29.0 From fb3e1c08e2b789cf067ddad3df37b4b5abcfc4ee Mon Sep 17 00:00:00 2001 From: Ningxin Hu Date: Wed, 14 Jan 2015 17:01:05 +0800 Subject: [PATCH 02/76] Reenable use of loadx and friends. To fix https://github.com/kripken/emscripten/issues/3123, adding loadx and friends into simd funcs. --- emscripten.py | 2 +- system/include/emscripten/xmmintrin.h | 26 -------------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/emscripten.py b/emscripten.py index 8c9d832a7d46c..b94dbaa76ce99 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1102,7 +1102,7 @@ def make_emulated_param(i): 'select', 'and', 'or', 'xor', 'not', 'splat', 'swizzle', 'shuffle', 'withX', 'withY', 'withZ', 'withW', - 'load', 'store'] + 'load', 'store', 'loadX', 'storeX', 'loadXY', 'storeXY', 'loadXYZ', 'storeXYZ'] simdfloatfuncs = simdfuncs + ['div', 'min', 'max', 'minNum', 'maxNum', 'sqrt', 'abs', 'fromInt32x4', 'fromInt32x4Bits']; simdintfuncs = simdfuncs + ['fromFloat32x4', 'fromFloat32x4Bits', diff --git a/system/include/emscripten/xmmintrin.h b/system/include/emscripten/xmmintrin.h index 7ffce688ca4a5..bbcc4ef9d03ed 100644 --- a/system/include/emscripten/xmmintrin.h +++ b/system/include/emscripten/xmmintrin.h @@ -53,21 +53,13 @@ _mm_load_ps(const float *__p) static __inline__ __m128 __attribute__((__always_inline__)) _mm_loadl_pi(__m128 __a, const void /*__m64*/ *__p) { -#if 0 // TODO: Re-enable these when we have full support return __builtin_shufflevector(emscripten_float32x4_loadxy(__p), __a, 0, 1, 4, 5); -#else - return (__m128){ ((const float*)__p)[0], ((const float*)__p)[1], __a[2], __a[3] }; -#endif } static __inline__ __m128 __attribute__((__always_inline__)) _mm_loadh_pi(__m128 __a, const void /*__m64*/ *__p) { -#if 0 // TODO: Re-enable these when we have full support return __builtin_shufflevector(__a, emscripten_float32x4_loadxy(__p), 0, 1, 4, 5); -#else - return (__m128){ __a[0], __a[1], ((const float*)__p)[0], ((const float*)__p)[1] }; -#endif } static __inline__ __m128 __attribute__((__always_inline__)) @@ -98,33 +90,19 @@ _mm_load_ps1(const float *__p) static __inline__ __m128 __attribute__((__always_inline__)) _mm_load_ss(const float *__p) { -#if 0 // TODO: Re-enable these when we have full support return emscripten_float32x4_loadx(__p); -#else - return (__m128){ *__p, 0.0f, 0.0f, 0.0f }; -#endif } static __inline__ void __attribute__((__always_inline__)) _mm_storel_pi(void /*__m64*/ *__p, __m128 __a) { -#if 0 // TODO: Re-enable these when we have full support emscripten_float32x4_storexy(__p, __a); -#else - ((float*)__p)[0] = __a[0]; - ((float*)__p)[1] = __a[1]; -#endif } static __inline__ void __attribute__((__always_inline__)) _mm_storeh_pi(void /*__m64*/ *__p, __m128 __a) { -#if 0 // TODO: Re-enable these when we have full support emscripten_float32x4_storexy(__p, __builtin_shufflevector(__a, __a, 2, 3, 0, 1)); -#else - ((float*)__p)[0] = __a[2]; - ((float*)__p)[1] = __a[3]; -#endif } static __inline__ void __attribute__((__always_inline__)) @@ -162,11 +140,7 @@ _mm_store_ps1(float *__p, __m128 __a) static __inline__ void __attribute__((__always_inline__)) _mm_store_ss(float *__p, __m128 __a) { -#if 0 // TODO: Re-enable these when we have full support emscripten_float32x4_storex(__p, __a); -#else - *__p = __a[0]; -#endif } static __inline__ void __attribute__((__always_inline__)) From bda8889d251039b367d1e4782dc1b622d1288257 Mon Sep 17 00:00:00 2001 From: Ningxin Hu Date: Wed, 14 Jan 2015 17:18:25 +0800 Subject: [PATCH 03/76] Fix _mm_loadl_pi implementation --- system/include/emscripten/xmmintrin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/xmmintrin.h b/system/include/emscripten/xmmintrin.h index bbcc4ef9d03ed..2958e5b581a20 100644 --- a/system/include/emscripten/xmmintrin.h +++ b/system/include/emscripten/xmmintrin.h @@ -53,7 +53,7 @@ _mm_load_ps(const float *__p) static __inline__ __m128 __attribute__((__always_inline__)) _mm_loadl_pi(__m128 __a, const void /*__m64*/ *__p) { - return __builtin_shufflevector(emscripten_float32x4_loadxy(__p), __a, 0, 1, 4, 5); + return __builtin_shufflevector(emscripten_float32x4_loadxy(__p), __a, 0, 1, 6, 7); } static __inline__ __m128 __attribute__((__always_inline__)) From acce0466b079585ec2517f31ec51a3b34cbc86bc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 11:47:12 -0800 Subject: [PATCH 04/76] fix test_memprof_requirements --- tests/test_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 6bc525a39c6d4..f0918bdb75c5b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7322,9 +7322,9 @@ def test_memprof_requirements(self): typeof STACKTOP === 'number' && typeof DYNAMIC_BASE === 'number' && typeof DYNAMICTOP === 'number') { - console.log('able to run memprof'); + Module.print('able to run memprof'); } else { - console.log('missing the required variables to run memprof'); + Module.print('missing the required variables to run memprof'); } } }); From 9a8acdef3eaa5e7ff94f58064b0e60624c67bc60 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 12:33:08 -0800 Subject: [PATCH 05/76] implement is_emterpreter in test runner, so it is usable outside of test_core --- tests/runner.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/runner.py b/tests/runner.py index 77d06ef6bbb82..f10a19baee837 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -65,6 +65,9 @@ class RunnerCore(unittest.TestCase): def skipme(self): # used by tests we ask on the commandline to be skipped, see right before call to unittest.main return self.skip('requested to be skipped') + def is_emterpreter(self): + return False + def setUp(self): Settings.reset() self.banned_js_engines = [] From 720f5d0efc52255f46db4abf57170927dad0f751 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 13:29:27 -0800 Subject: [PATCH 06/76] exit with the right return code in EMCONFIGURE_JS --- emcc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/emcc b/emcc index 2501c4a72577d..70d86fd04d4cf 100755 --- a/emcc +++ b/emcc @@ -257,7 +257,9 @@ if CONFIGURE_CONFIG or CMAKE_CONFIG: os.environ['EMMAKEN_JUST_CONFIGURE_RECURSE'] = '1' ret = subprocess.call(cmd) os.environ['EMMAKEN_JUST_CONFIGURE_RECURSE'] = '' - if not os.path.exists(target): exit(1) + if not os.path.exists(target): exit(ret) # note that emcc -c will cause target to have the wrong value here; + # but then, we don't care about bitcode outputs anyhow, below, so + # skipping to exit(ret) is fine if target.endswith('.js'): shutil.copyfile(target, target[:-3]) target = target[:-3] From 9be0275876217eacd30c848b1c7cea68d1a3d986 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 12:35:19 -0800 Subject: [PATCH 07/76] enable EMCONFIGURE_JS by default, i.e., run configure tests by compiling to js --- emcc | 8 +++++--- tests/test_other.py | 16 +++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/emcc b/emcc index 70d86fd04d4cf..0658079398175 100755 --- a/emcc +++ b/emcc @@ -186,9 +186,11 @@ CONFIGURE_CONFIG = (os.environ.get('EMMAKEN_JUST_CONFIGURE') or 'conftest.c' in CMAKE_CONFIG = 'CMakeFiles/cmTryCompileExec.dir' in ' '.join(sys.argv)# or 'CMakeCCompilerId' in ' '.join(sys.argv) if CONFIGURE_CONFIG or CMAKE_CONFIG: debug_configure = 0 # XXX use this to debug configure stuff. ./configure's generally hide our normal output including stderr so we write to a file - use_js = os.environ.get('EMCONFIGURE_JS') # whether we fake configure tests using clang - the local, native compiler - or not. if not we generate JS and use node with a shebang - # neither approach is perfect, you can try both, but may need to edit configure scripts in some cases - # XXX False is not fully tested yet + + # whether we fake configure tests using clang - the local, native compiler - or not. if not we generate JS and use node with a shebang + # neither approach is perfect, you can try both, but may need to edit configure scripts in some cases + # by default we configure in js, which can break on local filesystem access, etc., but is otherwise accurate + use_js = os.environ.get('EMCONFIGURE_JS') or 1 if debug_configure: tempout = '/tmp/emscripten_temp/out' diff --git a/tests/test_other.py b/tests/test_other.py index 95bc1a773194b..6a57fa42700db 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4632,13 +4632,15 @@ def test(args, expected): def test_emconfigure_js_o(self): # issue 2994 - try: - os.environ['EMCONFIGURE_JS'] = '1' - Popen([PYTHON, path_from_root('emconfigure'), PYTHON, EMCC, '-c', '-o', 'a.o', path_from_root('tests', 'hello_world.c')]).communicate() - Popen([PYTHON, EMCC, 'a.o']).communicate() - assert 'hello, world!' in run_js(self.in_dir('a.out.js')) - finally: - del os.environ['EMCONFIGURE_JS'] + for i in [0, 1]: + print i + try: + os.environ['EMCONFIGURE_JS'] = str(i) + Popen([PYTHON, path_from_root('emconfigure'), PYTHON, EMCC, '-c', '-o', 'a.o', path_from_root('tests', 'hello_world.c')]).communicate() + Popen([PYTHON, EMCC, 'a.o']).communicate() + assert 'hello, world!' in run_js(self.in_dir('a.out.js')) + finally: + del os.environ['EMCONFIGURE_JS'] def test_emcc_c_multi(self): def test(args, llvm_opts=None): From 21e91d472e8feff38fe786aa06b76ea18cc2b54f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 14:21:52 -0800 Subject: [PATCH 08/76] pass assertions to emterpretify --- emcc | 2 ++ tools/emterpretify.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/emcc b/emcc index 0658079398175..31cb5473545bd 100755 --- a/emcc +++ b/emcc @@ -1592,6 +1592,8 @@ try: args += ['ASYNC=1'] if profiling or profiling_funcs: args += ['PROFILING=1'] + if shared.Settings.ASSERTIONS: + args += ['ASSERTIONS=1'] execute(args) final = final + '.em.js' finally: diff --git a/tools/emterpretify.py b/tools/emterpretify.py index f1a2e85a2cf14..d16a4ef982f55 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -721,7 +721,7 @@ def process(code): external_emterpreted_funcs = filter(lambda func: func in tabled_funcs or func in exported_funcs or func in reachable_funcs, emterpreted_funcs) # process functions, generating bytecode - shared.Building.js_optimizer(infile, ['emterpretify'], extra_info={ 'emterpretedFuncs': list(emterpreted_funcs), 'externalEmterpretedFuncs': list(external_emterpreted_funcs), 'opcodes': OPCODES, 'ropcodes': ROPCODES, 'ASYNC': ASYNC, 'PROFILING': PROFILING }, output_filename=temp, just_concat=True) + shared.Building.js_optimizer(infile, ['emterpretify'], extra_info={ 'emterpretedFuncs': list(emterpreted_funcs), 'externalEmterpretedFuncs': list(external_emterpreted_funcs), 'opcodes': OPCODES, 'ropcodes': ROPCODES, 'ASYNC': ASYNC, 'PROFILING': PROFILING, 'ASSERTIONS': ASSERTIONS }, output_filename=temp, just_concat=True) # load the module and modify it asm = asm_module.AsmModule(temp) From 765f467c7338d74ee4eabd164b5112ad11cfcd72 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 14:23:35 -0800 Subject: [PATCH 09/76] add asserts on not being in a sync mode in non-emterpreted code --- tests/test_browser.py | 2 +- tools/js-optimizer.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 7156d0e4c3cd6..e9eda326e3048 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2479,7 +2479,7 @@ def test_emterpreter_async_virtual_2(self): self.btest('emterpreter_async_virtual_2.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O3', '-s', 'ASSERTIONS=1', '-s', 'SAFE_HEAP=1', '-profiling']) def zzztest_emterpreter_async_bad(self): - self.btest('emterpreter_async_bad.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O1', '-s', 'EMTERPRETIFY_BLACKLIST=["_middle"]']) + self.btest('emterpreter_async_bad.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O1', '-s', 'EMTERPRETIFY_BLACKLIST=["_middle"]', '-s', 'ASSERTIONS=1']) def test_modularize(self): for opts in [[], ['-O1'], ['-O2', '-profiling'], ['-O2']]: diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index d89484057ab28..3f3524bf4f6be 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6130,6 +6130,7 @@ function emterpretify(ast) { var ROPCODES = extraInfo.ropcodes; var ASYNC = extraInfo.ASYNC; var PROFILING = extraInfo.PROFILING; + var ASSERTIONS = extraInfo.ASSERTIONS; var RELATIVE_BRANCHES = set('BR', 'BRT', 'BRF'); var ABSOLUTE_BRANCHES = set('BRA', 'BRTA', 'BRFA'); @@ -7376,6 +7377,33 @@ function emterpretify(ast) { var ignore = !(func[1] in EMTERPRETED_FUNCS); if (ignore) { + // we are not emterpreting this function + if (ASYNC && ASSERTIONS) { + // we need to be careful to never enter non-emterpreted code while doing an async save/restore, + // which is what happens if non-emterpreted code is on the stack while we attempt to save + + traverse(func, function(node, type) {}, function(node, type) { // post-traversal + var callType = ASM_NONE; + var temp = null; + if (type === 'binary' && node[1] === '|' && node[3][0] === 'num' && node[3][1] === 0 && + node[2][0] === 'call') { + // int-coerced call + callType = ASM_INT; + temp = 'tempInt'; + } else if (type === 'unary-prefix' && node[1] === '+' && node[2][0] === 'call') { + // double-coerced call + callType = ASM_DOUBLE; + temp = 'tempDouble'; + } + if (callType !== ASM_NONE && (node[2][1] !== 'name' || !isMathFunc(node[2][1][0]))) { + // assign to temp, assert, return proper value: temp = call() , (asyncState ? abort() : temp) + node[2] = ['seq', + ['assign', null, ['name', temp], makeAsmCoercion(node[2], callType)], + ['conditional', ['name', 'asyncState'], makeAsmCoercion(['call', ['name', 'abort'], [['num', '-12']]], ASM_INT), ['name', temp]] + ]; + } + }); + } print(astToSrc(func)); } From 753c92b65f877d331dff2739e8aa2bce4cb20ac0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 15:13:19 -0800 Subject: [PATCH 10/76] improve abort() message --- src/postamble.js | 19 ++++++++++++++----- tests/test_core.py | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index 5ba8dd4ff9a70..72d16296317a0 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -203,10 +203,15 @@ function exit(status) { } Module['exit'] = Module.exit = exit; -function abort(text) { - if (text) { - Module.print(text); - Module.printErr(text); +var abortDecorators = []; + +function abort(what) { + if (what !== undefined) { + Module.print(what); + Module.printErr(what); + what = JSON.stringify(what) + } else { + what = ''; } ABORT = true; @@ -218,7 +223,11 @@ function abort(text) { var extra = ''; #endif - throw 'abort() at ' + stackTrace() + extra; + var output = 'abort(' + what + ') at ' + stackTrace() + extra; + abortDecorators.forEach(function(decorator) { + output = decorator(output, what); + }); + throw output; } Module['abort'] = Module.abort = abort; diff --git a/tests/test_core.py b/tests/test_core.py index f0918bdb75c5b..77bbf59e2b977 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2849,7 +2849,7 @@ def test_intentional_fault(self): return *(volatile char *)0; } ''' - self.do_run(src, 'fault on write to 0' if not Settings.ASM_JS else 'abort()') + self.do_run(src, 'abort()' if self.run_name != 'asm2g' else 'abort("segmentation fault') def test_trickystring(self): test_path = path_from_root('tests', 'core', 'test_trickystring') From 5159305f0731fbef2662e6d3f973dacc144caca2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 15:34:21 -0800 Subject: [PATCH 11/76] add (fairly) clear error reporting about emterpreter-async errors with non-emterpreted code on the stack --- src/library_async.js | 12 ++++++ tests/emterpreter_async_bad.cpp | 15 ++++--- tests/test_browser.py | 6 ++- tests/test_core.py | 2 + tools/js-optimizer.js | 73 +++++++++++++++++++++++++-------- 5 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/library_async.js b/src/library_async.js index fe065bc61ddd5..02d2ccc841f37 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -203,10 +203,22 @@ mergeInto(LibraryManager.library, { // if they are the first call or the second. The second typically does nothing, but // if there is a return value it could return it, etc. $EmterpreterAsync: { + initted: false, state: 0, // 0 - nothing // 1 - saving // 2 - loading + ensureInit: function() { + if (this.initted) return; + this.initted = true; + abortDecorators.push(function(output, what) { + if (what == -12 && EmterpreterAsync.state !== 0) { + return output + '\nThis error happened during an emterpreter-async save or load of the stack. Was there non-emterpreted code on the stack during save (which is unallowed)?'; + } + return output; + }); + }, setState: function(s) { + this.ensureInit(); this.state = s; asm.setAsyncState(s); }, diff --git a/tests/emterpreter_async_bad.cpp b/tests/emterpreter_async_bad.cpp index 053b9060dda72..30991fa8ecb82 100644 --- a/tests/emterpreter_async_bad.cpp +++ b/tests/emterpreter_async_bad.cpp @@ -4,9 +4,7 @@ extern "C" { -int result = 1; - -void finish() { +void EMSCRIPTEN_KEEPALIVE finish(int result) { REPORT_RESULT(); } @@ -18,7 +16,7 @@ void __attribute__((noinline)) run_loop() { printf("frame: %d\n", ++counter); emscripten_sleep(100); if (counter == 10) { - finish(); + finish(123); // this should not happen, we should fail! break; } } @@ -28,10 +26,17 @@ void __attribute__((noinline)) middle() { run_loop(); printf("after run_loop, counter: %d\n", counter); assert(counter == 10); - result = 99; + assert(0); // we should never get here! } int main() { + EM_ASM({ + window.onerror = function(err) { + assert(err.toString().indexOf('This error happened during an emterpreter-async save or load of the stack') > 0, 'expect good error message'); + Module._finish(1); + }; + }); + middle(); } diff --git a/tests/test_browser.py b/tests/test_browser.py index e9eda326e3048..4fc1732785697 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2478,8 +2478,10 @@ def test_emterpreter_async_virtual(self): def test_emterpreter_async_virtual_2(self): self.btest('emterpreter_async_virtual_2.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O3', '-s', 'ASSERTIONS=1', '-s', 'SAFE_HEAP=1', '-profiling']) - def zzztest_emterpreter_async_bad(self): - self.btest('emterpreter_async_bad.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O1', '-s', 'EMTERPRETIFY_BLACKLIST=["_middle"]', '-s', 'ASSERTIONS=1']) + def test_emterpreter_async_bad(self): + for opts in [0, 1, 2]: + print opts + self.btest('emterpreter_async_bad.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-s', 'EMTERPRETIFY_BLACKLIST=["_middle"]', '-s', 'ASSERTIONS=1']) def test_modularize(self): for opts in [[], ['-O1'], ['-O2', '-profiling'], ['-O2']]: diff --git a/tests/test_core.py b/tests/test_core.py index 77bbf59e2b977..a41390f259d36 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5255,6 +5255,8 @@ 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 + if self.is_emterpreter(): + self.emcc_args += ['-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'ASSERTIONS=1'] # 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... diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 3f3524bf4f6be..03a9726f94812 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6120,6 +6120,13 @@ function findUninitializedVars(func, asmData) { return bad; } +function trample(x, y) { // x = y, by trampling it + for (var i = 0; i < y.length; i++) { + x[i] = y[i]; + } + x.length = y.length; +} + // Converts functions into binary format to be run by an emterpreter function emterpretify(ast) { emitAst = false; @@ -7382,27 +7389,57 @@ function emterpretify(ast) { // we need to be careful to never enter non-emterpreted code while doing an async save/restore, // which is what happens if non-emterpreted code is on the stack while we attempt to save - traverse(func, function(node, type) {}, function(node, type) { // post-traversal + // add asserts right after each call + var stack = []; + traverse(func, function(node, type) { + stack.push(node); + }, function(node, type) { // post-traversal + stack.pop(); + if (type !== 'call') return; + if (node[1][0] === 'name' && isMathFunc(node[1][1])) return; var callType = ASM_NONE; - var temp = null; - if (type === 'binary' && node[1] === '|' && node[3][0] === 'num' && node[3][1] === 0 && - node[2][0] === 'call') { - // int-coerced call - callType = ASM_INT; - temp = 'tempInt'; - } else if (type === 'unary-prefix' && node[1] === '+' && node[2][0] === 'call') { - // double-coerced call - callType = ASM_DOUBLE; - temp = 'tempDouble'; - } - if (callType !== ASM_NONE && (node[2][1] !== 'name' || !isMathFunc(node[2][1][0]))) { - // assign to temp, assert, return proper value: temp = call() , (asyncState ? abort() : temp) - node[2] = ['seq', - ['assign', null, ['name', temp], makeAsmCoercion(node[2], callType)], - ['conditional', ['name', 'asyncState'], makeAsmCoercion(['call', ['name', 'abort'], [['num', '-12']]], ASM_INT), ['name', temp]] - ]; + var parent = stack[stack.length-1]; + if (parent) { + var temp = null; + if (parent[0] === 'binary' && parent[1] === '|' && parent[3][0] === 'num' && parent[3][1] === 0 && + parent[2] === node) { + // int-coerced call + callType = ASM_INT; + temp = 'tempInt'; + } else if (parent[0] === 'unary-prefix' && parent[1] === '+' && parent[2] === node) { + // double-coerced call + callType = ASM_DOUBLE; + temp = 'tempDouble'; + } + // XXX fails on other coercions of odd types, like float32, simd, etc! + if (temp) { + // assign to temp, assert, return proper value: temp = call() , (asyncState ? abort() : temp) + trample(node, ['seq', + ['assign', null, ['name', temp], makeAsmCoercion(copy(node), callType)], + ['conditional', ['name', 'asyncState'], makeAsmCoercion(['call', ['name', 'abort'], [['num', '-12']]], ASM_INT), ['name', temp]] + ]); + return; + } } + // no important parent + trample(node, ['seq', + copy(node), + ['conditional', ['name', 'asyncState'], makeAsmCoercion(['call', ['name', 'abort'], [['num', '-12']]], ASM_INT), ['num', 0]] + ]); }); + // add an assert in the prelude of the function + var stats = getStatements(func); + for (var i = 0; i < stats.length; i++) { + var node = stats[i]; + if (node[0] == 'stat') node = node[1]; + if (node[0] !== 'var' && node[0] !== 'assign') { + stats.splice(i, 0, ['stat', + ['conditional', ['name', 'asyncState'], makeAsmCoercion(['call', ['name', 'abort'], [['num', '-12']]], ASM_INT), ['num', 0]] + ]); + break; + } + } + // perhaps also add at loop headers? TODO } print(astToSrc(func)); } From 41accb424e072804cd4d38827e2c72c0fb967833 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 15:37:55 -0800 Subject: [PATCH 12/76] add more emterpreter-async test coverage --- tests/test_browser.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 4fc1732785697..281a3f9c0d6bf 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2467,19 +2467,27 @@ def test_sdl2_canvas_write(self): self.btest('sdl2_canvas_write.cpp', expected='0', args=['-s', 'USE_SDL=2']) def test_emterpreter_async(self): - self.btest('emterpreter_async.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O3', '-g2']) + for opts in [0, 1, 2, 3]: + print opts + self.btest('emterpreter_async.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-g2']) def test_emterpreter_async_2(self): - self.btest('emterpreter_async_2.cpp', '47', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O3']) + for opts in [0, 1, 2, 3]: + print opts + self.btest('emterpreter_async_2.cpp', '47', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts)]) def test_emterpreter_async_virtual(self): - self.btest('emterpreter_async_virtual.cpp', '5', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O3', '-profiling']) + for opts in [0, 1, 2, 3]: + print opts + self.btest('emterpreter_async_virtual.cpp', '5', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-profiling']) def test_emterpreter_async_virtual_2(self): - self.btest('emterpreter_async_virtual_2.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O3', '-s', 'ASSERTIONS=1', '-s', 'SAFE_HEAP=1', '-profiling']) + for opts in [0, 1, 2, 3]: + print opts + self.btest('emterpreter_async_virtual_2.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-s', 'ASSERTIONS=1', '-s', 'SAFE_HEAP=1', '-profiling']) def test_emterpreter_async_bad(self): - for opts in [0, 1, 2]: + for opts in [0, 1, 2, 3]: print opts self.btest('emterpreter_async_bad.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-s', 'EMTERPRETIFY_BLACKLIST=["_middle"]', '-s', 'ASSERTIONS=1']) From 024796577a344d4365ef6e6857053c6c73edbdd1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 16:00:39 -0800 Subject: [PATCH 13/76] add more emterpreter-async test coverage, and fix a spurious assert --- src/library_async.js | 9 +++++++-- tests/test_browser.py | 4 +--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/library_async.js b/src/library_async.js index 02d2ccc841f37..9c44a8782b21d 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -207,12 +207,13 @@ mergeInto(LibraryManager.library, { state: 0, // 0 - nothing // 1 - saving // 2 - loading + saveStack: '', ensureInit: function() { if (this.initted) return; this.initted = true; abortDecorators.push(function(output, what) { if (what == -12 && EmterpreterAsync.state !== 0) { - return output + '\nThis error happened during an emterpreter-async save or load of the stack. Was there non-emterpreted code on the stack during save (which is unallowed)?'; + return output + '\nThis error happened during an emterpreter-async save or load of the stack. Was there non-emterpreted code on the stack during save (which is unallowed)? This is what the stack looked like when we tried to save it: ' + EmterpreterAsync.saveStack; } return output; }); @@ -233,11 +234,15 @@ mergeInto(LibraryManager.library, { assert(EmterpreterAsync.state === 1); // copy the stack back in and resume HEAP32.set(stack, EMTSTACKTOP>>2); - EmterpreterAsync.setState(2); +#if ASSERTIONS + EmterpreterAsync.setState(0); // set it to 0 just so stackSave is ok to run assert(stacktop === asm.stackSave()); // nothing should have modified the stack meanwhile +#endif + EmterpreterAsync.setState(2); asm.emterpret(stack[0]); // pc of the first function, from which we can reconstruct the rest, is at position 0 on the stack }); EmterpreterAsync.setState(1); + this.saveStack = stackTrace(); } else { // nothing to do here, the stack was just recreated. reset the state. assert(EmterpreterAsync.state === 2); diff --git a/tests/test_browser.py b/tests/test_browser.py index 281a3f9c0d6bf..33aa8e8275ead 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2472,9 +2472,7 @@ def test_emterpreter_async(self): self.btest('emterpreter_async.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-g2']) def test_emterpreter_async_2(self): - for opts in [0, 1, 2, 3]: - print opts - self.btest('emterpreter_async_2.cpp', '47', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts)]) + self.btest('emterpreter_async_2.cpp', '47', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O3']) def test_emterpreter_async_virtual(self): for opts in [0, 1, 2, 3]: From 87a7349b4636fad28a314916ab20e95bf1fb5ae8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 9 Feb 2015 16:37:35 -0800 Subject: [PATCH 14/76] mention emscripten_force_exit --- src/postamble.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/postamble.js b/src/postamble.js index 72d16296317a0..6f78f0a6f1ea7 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -166,7 +166,7 @@ Module['run'] = Module.run = run; function exit(status) { if (Module['noExitRuntime']) { #if ASSERTIONS - Module.printErr('exit(' + status + ') called, but noExitRuntime, so not exiting'); + Module.printErr('exit(' + status + ') called, but noExitRuntime, so not exiting (you can use emscripten_force_exit, if you want to force a true shutdown)'); #endif return; } From df2e335d06562fff49520264314da6277459aca6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 10 Feb 2015 13:14:28 -0800 Subject: [PATCH 15/76] put emterpreter-async asserts support code behind ASSERTIONS --- src/library_async.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/library_async.js b/src/library_async.js index 9c44a8782b21d..6e4f78b05b5b8 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -211,12 +211,14 @@ mergeInto(LibraryManager.library, { ensureInit: function() { if (this.initted) return; this.initted = true; +#if ASSERTIONS abortDecorators.push(function(output, what) { if (what == -12 && EmterpreterAsync.state !== 0) { return output + '\nThis error happened during an emterpreter-async save or load of the stack. Was there non-emterpreted code on the stack during save (which is unallowed)? This is what the stack looked like when we tried to save it: ' + EmterpreterAsync.saveStack; } return output; }); +#endif }, setState: function(s) { this.ensureInit(); @@ -242,7 +244,9 @@ mergeInto(LibraryManager.library, { asm.emterpret(stack[0]); // pc of the first function, from which we can reconstruct the rest, is at position 0 on the stack }); EmterpreterAsync.setState(1); +#if ASSERTIONS this.saveStack = stackTrace(); +#endif } else { // nothing to do here, the stack was just recreated. reset the state. assert(EmterpreterAsync.state === 2); From d8785b49101fc3cdd2b896b1b80d49be57725a4a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 10 Feb 2015 13:51:18 -0800 Subject: [PATCH 16/76] handle the main loop properly during an emterpreter sync save/load --- src/library_async.js | 9 +++++++ tests/emterpreter_async_mainloop.cpp | 35 ++++++++++++++++++++++++++++ tests/test_browser.py | 5 ++++ tools/js-optimizer.js | 6 +++-- 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/emterpreter_async_mainloop.cpp diff --git a/src/library_async.js b/src/library_async.js index 6e4f78b05b5b8..7ecbcf3b5cc81 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -202,6 +202,7 @@ mergeInto(LibraryManager.library, { // to the async function here! Therefore sleep etc. detect the state, so they know // if they are the first call or the second. The second typically does nothing, but // if there is a return value it could return it, etc. + $EmterpreterAsync__deps: ['$Browser'], $EmterpreterAsync: { initted: false, state: 0, // 0 - nothing @@ -241,12 +242,20 @@ mergeInto(LibraryManager.library, { assert(stacktop === asm.stackSave()); // nothing should have modified the stack meanwhile #endif EmterpreterAsync.setState(2); + // Resume the main loop + if (Browser.mainLoop.func) { + Browser.mainLoop.resume(); + } asm.emterpret(stack[0]); // pc of the first function, from which we can reconstruct the rest, is at position 0 on the stack }); EmterpreterAsync.setState(1); #if ASSERTIONS this.saveStack = stackTrace(); #endif + // Pause the main loop, until we resume + if (Browser.mainLoop.func) { + Browser.mainLoop.pause(); + } } else { // nothing to do here, the stack was just recreated. reset the state. assert(EmterpreterAsync.state === 2); diff --git a/tests/emterpreter_async_mainloop.cpp b/tests/emterpreter_async_mainloop.cpp new file mode 100644 index 0000000000000..b776c2491b7f2 --- /dev/null +++ b/tests/emterpreter_async_mainloop.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +extern "C" { + +void EMSCRIPTEN_KEEPALIVE finish(int result) { + REPORT_RESULT(); +} + +int counter = 0; +int nesting = 0; + +void iter() { + printf("frame: %d\n", ++counter); + + // ensure we don't 'recurse' with the main loop sending is back in before the synchronous operation callback finishes the rest of this trace + assert(nesting == 0); + nesting++; + emscripten_sleep(500); + assert(nesting == 1); + nesting = 0; + + if (counter == 10) { + finish(121); // if we got here without hitting any assertions, all is well + emscripten_cancel_main_loop(); + } +} + +int main() { + emscripten_set_main_loop(iter, 0, 0); +} + +} + diff --git a/tests/test_browser.py b/tests/test_browser.py index 33aa8e8275ead..cf8ea42c6f0ce 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2489,6 +2489,11 @@ def test_emterpreter_async_bad(self): print opts self.btest('emterpreter_async_bad.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-s', 'EMTERPRETIFY_BLACKLIST=["_middle"]', '-s', 'ASSERTIONS=1']) + def test_emterpreter_async_mainloop(self): + for opts in [0, 1, 2, 3]: + print opts + self.btest('emterpreter_async_mainloop.cpp', '121', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts)]) + def test_modularize(self): for opts in [[], ['-O1'], ['-O2', '-profiling'], ['-O2']]: for args, code in [ diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 03a9726f94812..3f77507411540 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -7385,9 +7385,11 @@ function emterpretify(ast) { if (ignore) { // we are not emterpreting this function - if (ASYNC && ASSERTIONS) { + if (ASYNC && ASSERTIONS && !/^dynCall_/.test(func[1])) { // we need to be careful to never enter non-emterpreted code while doing an async save/restore, - // which is what happens if non-emterpreted code is on the stack while we attempt to save + // which is what happens if non-emterpreted code is on the stack while we attempt to save. + // note that we special-case dynCall, which *can* be on the stack, they are just bridges; what + // matters is where they go // add asserts right after each call var stack = []; From f674fc31f0021955615f815d975cc12e884becc7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 10 Feb 2015 14:28:32 -0800 Subject: [PATCH 17/76] notice compilation errors in btest() in test runner --- tests/runner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/runner.py b/tests/runner.py index f10a19baee837..d3f2e8b233615 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -726,6 +726,7 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc args = args + ['--pre-js', 'reftest.js', '-s', 'GL_TESTING=1'] all_args = [PYTHON, EMCC, temp_filepath, '-o', outfile] + args #print 'all args:', all_args + try_delete(outfile) Popen(all_args).communicate() assert os.path.exists(outfile) if post_build: post_build() From 5368acded7e2dd46d872956f47d869573795812d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 10 Feb 2015 14:56:22 -0800 Subject: [PATCH 18/76] handle emscripten_async_call etc. during sleep, by pausing all safeSet* operations --- src/library_async.js | 10 ++++- src/library_browser.js | 39 ++++++++++++++++-- tests/emterpreter_async_with_manual.cpp | 55 +++++++++++++++++++++++++ tests/test_browser.py | 5 +++ 4 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 tests/emterpreter_async_with_manual.cpp diff --git a/src/library_async.js b/src/library_async.js index 7ecbcf3b5cc81..2312b1f4f40cc 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -247,6 +247,10 @@ mergeInto(LibraryManager.library, { Browser.mainLoop.resume(); } asm.emterpret(stack[0]); // pc of the first function, from which we can reconstruct the rest, is at position 0 on the stack + if (EmterpreterAsync.state === 0) { + // if we did *not* do another async operation, then we know that nothing is conceptually on the stack now, and we can re-allow async callbacks as well as run the queued ones right now + Browser.resumeAsyncCallbacks(); + } }); EmterpreterAsync.setState(1); #if ASSERTIONS @@ -256,6 +260,7 @@ mergeInto(LibraryManager.library, { if (Browser.mainLoop.func) { Browser.mainLoop.pause(); } + Browser.pauseAsyncCallbacks(); } else { // nothing to do here, the stack was just recreated. reset the state. assert(EmterpreterAsync.state === 2); @@ -267,7 +272,10 @@ mergeInto(LibraryManager.library, { emscripten_sleep__deps: ['$EmterpreterAsync'], emscripten_sleep: function(ms) { EmterpreterAsync.handle(function(resume) { - Browser.safeSetTimeout(resume, ms); + setTimeout(function() { + if (ABORT) return; // do this manually; we can't call into Browser.safeSetTimeout, because that is paused/resumed! + resume(); + }, ms); }); }, diff --git a/src/library_browser.js b/src/library_browser.js index 08fa15fec4812..d4bba0aa8fd6c 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -418,22 +418,53 @@ mergeInto(LibraryManager.library, { }; }, - // abort-aware versions + // abort and pause-aware versions TODO: build main loop on top of this? + + allowAsyncCallbacks: true, + queuedAsyncCallbacks: [], + + pauseAsyncCallbacks: function() { + Browser.allowAsyncCallbacks = false; + }, + resumeAsyncCallbacks: function() { // marks future callbacks as ok to execute, and synchronously runs any remaining ones right now + Browser.allowAsyncCallbacks = true; + if (Browser.queuedAsyncCallbacks.length > 0) { + var callbacks = Browser.queuedAsyncCallbacks; + Browser.queuedAsyncCallbacks = []; + callbacks.forEach(function(func) { + func(); + }); + } + }, + safeRequestAnimationFrame: function(func) { return Browser.requestAnimationFrame(function() { - if (!ABORT) func(); + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } }); }, safeSetTimeout: function(func, timeout) { Module['noExitRuntime'] = true; return setTimeout(function() { - if (!ABORT) func(); + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } }, timeout); }, safeSetInterval: function(func, timeout) { Module['noExitRuntime'] = true; return setInterval(function() { - if (!ABORT) func(); + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } // drop it on the floor otherwise, next interval will kick in }, timeout); }, diff --git a/tests/emterpreter_async_with_manual.cpp b/tests/emterpreter_async_with_manual.cpp new file mode 100644 index 0000000000000..01baa21ce198e --- /dev/null +++ b/tests/emterpreter_async_with_manual.cpp @@ -0,0 +1,55 @@ +#include +#include +#include + +extern "C" { + +void EMSCRIPTEN_KEEPALIVE finish(int result) { + REPORT_RESULT(); +} + +int counter = 0; +int nesting = 0; +int acall_counter = 0; + +void EMSCRIPTEN_KEEPALIVE acall(void *arg) { + assert(nesting == 0); + printf("an async call %d\n", ++acall_counter); +} + +void iter() { + printf("frame: %d\n", ++counter); + + emscripten_async_call(acall, 0, counter % 2); // 0 or 1 millis, try to execute this *before* the sync callback finishes, which would be bad + + // ensure we don't 'recurse' with the main loop sending is back in before the synchronous operation callback finishes the rest of this trace + assert(nesting == 0); + nesting++; + emscripten_sleep(500); + assert(nesting == 1); + nesting = 2; + + if (counter % 3 == 2) { + // second sleep every 3rd frame, to test sleep when returning from a sleep + printf(" (second sleep)\n"); + assert(nesting == 2); + nesting = 3; + emscripten_sleep(1000); + assert(nesting == 3); + }; + + nesting = 0; + + if (counter == 10) { + assert(acall_counter == 9 /* the tenth is about to execute, but not yet! */ ); + finish(121); // if we got here without hitting any assertions, all is well + emscripten_cancel_main_loop(); + } +} + +int main() { + emscripten_set_main_loop(iter, 3, 0); +} + +} + diff --git a/tests/test_browser.py b/tests/test_browser.py index cf8ea42c6f0ce..25fa2aded5440 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2494,6 +2494,11 @@ def test_emterpreter_async_mainloop(self): print opts self.btest('emterpreter_async_mainloop.cpp', '121', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts)]) + def test_emterpreter_async_with_manual(self): + for opts in [0, 1, 2, 3]: + print opts + self.btest('emterpreter_async_with_manual.cpp', '121', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-O' + str(opts), '-s', 'EMTERPRETIFY_BLACKLIST=["_acall"]']) + def test_modularize(self): for opts in [[], ['-O1'], ['-O2', '-profiling'], ['-O2']]: for args, code in [ From 3a0e572509922dca7c1800b67876e7515a22837f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 10 Feb 2015 15:26:27 -0800 Subject: [PATCH 19/76] differentiate sleep with and without yield in emterpreter-async --- .../docs/api_reference/emscripten.h.rst | 26 +++++++++++++++++-- src/library_async.js | 13 +++++++--- system/include/emscripten/emscripten.h | 1 + tests/test_core.py | 6 ++--- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index 9d4453759147c..9c3208c46bfff 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -1073,6 +1073,27 @@ Typedefs .. note:: It is better to avoid unaligned operations, but if you are reading from a packed stream of bytes or such, these types may be useful! + +Emterpreter-Async functions +=========================== + +Emterpreter-async functions are asynchronous functions that appear synchronously in C, the linker flags `-s EMTERPRETIFY -s EMTERPRETIFY_ASYNC=1` are required to use these functions. See `Emterpreter `_ for more details. + +Functions +--------- + +.. c:function:: void emscripten_sleep(unsigned int ms) + + Sleep for `ms` milliseconds. This is a normal "synchronous" sleep, which blocks all other operations while it runs. In other words, if + there are other async events waiting to happen, they will not happen during this sleep, which makes sense as conceptually this code is + on the stack (that's how it looks in the C source code). If you do want things to happen while sleeping, see ``emscripten_sleep_with_yield``. + +.. c:function:: void emscripten_sleep_with_yield(unsigned int ms) + + Sleep for `ms` milliseconds, while allowing other asynchronous operations, e.g. caused by ``emscripten_async_call``, to run normally, during + this sleep. Note that this method **does** still block the main loop, as otherwise it could recurse, if you are calling this method from it. + Even so, you should use this method carefully: the order of execution is potentially very confusing this way. + Asyncify functions ================== @@ -1084,12 +1105,12 @@ Typedefs .. c:type:: emscripten_coroutine - A handle to the strcture used by coroutine supporting functions. + A handle to the structure used by coroutine supporting functions. Functions --------- -.. c::function:: void emscripten_sleep(unsinged int ms) +.. c::function:: void emscripten_sleep(unsigned int ms) Sleep for `ms` milliseconds. @@ -1107,3 +1128,4 @@ Functions This function should only be called in a coroutine created by `emscripten_coroutine_create`, when it called, the coroutine is paused and the caller will continue. + diff --git a/src/library_async.js b/src/library_async.js index 2312b1f4f40cc..cc04153f1e478 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -226,7 +226,7 @@ mergeInto(LibraryManager.library, { this.state = s; asm.setAsyncState(s); }, - handle: function(doAsyncOp) { + handle: function(doAsyncOp, yieldDuring) { Module['noExitRuntime'] = true; if (EmterpreterAsync.state === 0) { // save the stack we want to resume. this lets other code run in between @@ -247,7 +247,7 @@ mergeInto(LibraryManager.library, { Browser.mainLoop.resume(); } asm.emterpret(stack[0]); // pc of the first function, from which we can reconstruct the rest, is at position 0 on the stack - if (EmterpreterAsync.state === 0) { + if (!yieldDuring && EmterpreterAsync.state === 0) { // if we did *not* do another async operation, then we know that nothing is conceptually on the stack now, and we can re-allow async callbacks as well as run the queued ones right now Browser.resumeAsyncCallbacks(); } @@ -260,7 +260,7 @@ mergeInto(LibraryManager.library, { if (Browser.mainLoop.func) { Browser.mainLoop.pause(); } - Browser.pauseAsyncCallbacks(); + if (!yieldDuring) Browser.pauseAsyncCallbacks(); } else { // nothing to do here, the stack was just recreated. reset the state. assert(EmterpreterAsync.state === 2); @@ -279,6 +279,13 @@ mergeInto(LibraryManager.library, { }); }, + emscripten_sleep_with_yield__deps: ['$EmterpreterAsync'], + emscripten_sleep_with_yield: function(ms) { + EmterpreterAsync.handle(function(resume) { + Browser.safeSetTimeout(resume, ms); + }, true); + }, + #else emscripten_sleep: function() { throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_sleep'; diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 98a330d7d704a..8836996bb4c5b 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -266,6 +266,7 @@ double emscripten_asm_const_double(const char *code, ...); #if __EMSCRIPTEN__ void emscripten_sleep(unsigned int ms); +void emscripten_sleep_with_yield(unsigned int ms); #else #define emscripten_sleep SDL_Delay #endif diff --git a/tests/test_core.py b/tests/test_core.py index a41390f259d36..b17ef34e94b6b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7219,10 +7219,10 @@ def test_async(self): printf("Hello"); emscripten_async_call(f, &i, 1); printf("World"); - emscripten_sleep(100); - printf("%d\n", i); + emscripten_%s(100); + printf("%%d\n", i); } -''' +''' % ('sleep_with_yield' if self.is_emterpreter() else 'sleep') if not self.is_emterpreter(): Settings.ASYNCIFY = 1 From 25b0d1efec455014ace2fbd51d209d4d239c6c2c Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Wed, 11 Feb 2015 20:40:46 +0700 Subject: [PATCH 20/76] [tracing] Add some initial support for Google WTF. Info on Google WTF can be found at: http://google.github.io/tracing-framework/ --- site/source/docs/api_reference/trace.h.rst | 34 ++++++ src/library_trace.js | 133 ++++++++++++++++----- system/include/emscripten/trace.h | 6 + 3 files changed, 141 insertions(+), 32 deletions(-) diff --git a/site/source/docs/api_reference/trace.h.rst b/site/source/docs/api_reference/trace.h.rst index 5aeac6a3240b8..a1026ab6f756a 100644 --- a/site/source/docs/api_reference/trace.h.rst +++ b/site/source/docs/api_reference/trace.h.rst @@ -9,6 +9,11 @@ what is going on inside of your application, in particular with respect to memory usage (which is otherwise not available to traditional browser performance tools). +The tracing API can talk to a custom collection server (see `Running the Server`_ +for more details) or it can talk with the `Google Web Tracing Framework`_. +When talking with the `Google Web Tracing Framework`_, a subset of the data +available is collected. + .. contents:: table of contents :local: @@ -46,6 +51,14 @@ To initialize the tracing API, you call :c:func:`emscripten_trace_configure`: emscripten_trace_configure("http://127.0.0.1:5000/", "MyApplication"); +If you are simply going to use the tracing API with the `Google Web Tracing +Framework`_, then you can just call :c:func:`emscripten_trace_configure_for_google_wtf` +instead: + +.. code-block:: c + + emscripten_trace_configure_for_google_wtf(); + If you have the concept of a username or have some other way to identify a given user of the application, then passing that to the tracing API can make it easier to identify sessions in the collector server: @@ -289,6 +302,15 @@ Functions In most cases, the ``collector_url`` will be ``http://127.0.0.1:5000/``. +.. c:function:: void emscripten_configure_for_google_wtf(void) + + :rtype: void + + Configure tracing to communicate with the `Google Web Tracing Framework`_. + + Not all features of the tracing are available within the Google WTF + tools. (Currently, only contexts, log messages and marks.) + .. c:function:: void emscripten_trace_set_enabled(bool enabled) :param enabled: Whether or not tracing is enabled. @@ -353,6 +375,17 @@ Functions *The server doesn't yet do enough with this data. This will improve in the future.* +.. c:function:: void emscripten_trace_mark(const char *message) + + :param message: The name of the mark begin emitted. + :type message: const char * + :rtype: void + + Record a mark in the timeline. This is primary for use with the + `Google Web Tracing Framework`_. + + The current timestamp is associated with this data. + .. c:function:: void emscripten_trace_report_error(const char *error) :param error: The error message being reported. @@ -548,3 +581,4 @@ Functions .. _emscripten-trace-collector: https://github.com/waywardmonkeys/emscripten-trace-collector .. _README.rst: https://github.com/waywardmonkeys/emscripten-trace-collector/blob/master/README.rst +.. _Google Web Tracing Framework: http://google.github.io/tracing-framework/ diff --git a/src/library_trace.js b/src/library_trace.js index 4a9d501ae4fc9..dbe7e711fd249 100644 --- a/src/library_trace.js +++ b/src/library_trace.js @@ -1,15 +1,21 @@ var LibraryTracing = { $EmscriptenTrace__deps: [ - 'emscripten_trace_js_configure', 'emscripten_trace_js_log_message', + 'emscripten_trace_js_configure', 'emscripten_trace_configure_for_google_wtf', 'emscripten_trace_js_enter_context', 'emscripten_trace_exit_context', + 'emscripten_trace_js_log_message', 'emscripten_trace_js_mark', 'emscripten_get_now' ], $EmscriptenTrace__postset: 'EmscriptenTrace.init()', $EmscriptenTrace: { - configured: false, - testing: false, worker: null, - enabled: false, + collectorEnabled: false, + googleWTFEnabled: false, + testingEnabled: false, + + googleWTFData: { + 'scopeStack': [], + 'cachedScopes': {} + }, DATA_VERSION: 1, @@ -38,9 +44,11 @@ var LibraryTracing = { init: function() { Module['emscripten_trace_configure'] = _emscripten_trace_js_configure; - Module['emscripten_trace_log_message'] = _emscripten_trace_js_log_message; + Module['emscripten_trace_configure_for_google_wtf'] = _emscripten_trace_configure_for_google_wtf; Module['emscripten_trace_enter_context'] = _emscripten_trace_js_enter_context; Module['emscripten_trace_exit_context'] = _emscripten_trace_exit_context; + Module['emscripten_trace_log_message'] = _emscripten_trace_js_log_message; + Module['emscripten_trace_mark'] = _emscripten_trace_js_mark; }, // Work around CORS issues ... @@ -73,26 +81,50 @@ var LibraryTracing = { 'session_id': session_id, 'url': collector_url }); EmscriptenTrace.configured = true; - EmscriptenTrace.enabled = true; + EmscriptenTrace.collectorEnabled = true; + EmscriptenTrace.postEnabled = true; }); EmscriptenTrace.post([EmscriptenTrace.EVENT_APPLICATION_NAME, application]); EmscriptenTrace.post([EmscriptenTrace.EVENT_SESSION_NAME, now.toISOString()]); }, configureForTest: function() { - EmscriptenTrace.testing = true; - EmscriptenTrace.enabled = true; + EmscriptenTrace.postEnabled = true; + EmscriptenTrace.testingEnabled = true; EmscriptenTrace.now = function() { return 0.0; }; }, + configureForGoogleWTF: function() { + if (window && window.wtf) { + EmscriptenTrace.googleWTFEnabled = true; + } else { + console.log('GOOGLE WTF NOT AVAILABLE TO ENABLE'); + } + }, + post: function(entry) { - if (EmscriptenTrace.configured && EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled && EmscriptenTrace.collectorEnabled) { EmscriptenTrace.worker.postMessage({ 'cmd': 'post', 'entry': entry }); - } else if (EmscriptenTrace.testing && EmscriptenTrace.enabled) { + } else if (EmscriptenTrace.postEnabled && EmscriptenTrace.testingEnabled) { Module.print('Tracing ' + entry); } }, + + googleWTFEnterScope: function(name) { + var scopeEvent = EmscriptenTrace.googleWTFData['cachedScopes'][name]; + if (!scopeEvent) { + scopeEvent = window.wtf.trace.events.createScope(name); + EmscriptenTrace.googleWTFData['cachedScopes'][name] = scopeEvent; + } + var scope = scopeEvent(); + EmscriptenTrace.googleWTFData['scopeStack'].push(scope); + }, + + googleWTFExitScope: function() { + var scope = EmscriptenTrace.googleWTFData['scopeStack'].pop(); + window.wtf.trace.leaveScope(scope); + } }, emscripten_trace_js_configure: function(collector_url, application) { @@ -108,8 +140,12 @@ var LibraryTracing = { EmscriptenTrace.configureForTest(); }, + emscripten_trace_configure_for_google_wtf: function() { + EmscriptenTrace.configureForGoogleWTF(); + }, + emscripten_trace_set_enabled: function(enabled) { - EmscriptenTrace.enabled = !!enabled; + EmscriptenTrace.postEnabled = !!enabled; }, emscripten_trace_set_session_username: function(username) { @@ -117,21 +153,21 @@ var LibraryTracing = { }, emscripten_trace_record_frame_start: function() { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_FRAME_START, now]); } }, emscripten_trace_record_frame_end: function() { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_FRAME_END, now]); } }, emscripten_trace_js_log_message: function(channel, message) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_LOG_MESSAGE, now, channel, message]); @@ -139,7 +175,7 @@ var LibraryTracing = { }, emscripten_trace_log_message: function(channel, message) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_LOG_MESSAGE, now, Pointer_stringify(channel), @@ -147,6 +183,28 @@ var LibraryTracing = { } }, + emscripten_trace_js_mark: function(message) { + if (EmscriptenTrace.postEnabled) { + var now = EmscriptenTrace.now(); + EmscriptenTrace.post([EmscriptenTrace.EVENT_LOG_MESSAGE, now, + "MARK", message]); + } + if (EmscriptenTrace.googleWTFEnabled) { + window.wtf.trace.mark(message); + } + }, + + emscripten_trace_mark: function(message) { + if (EmscriptenTrace.postEnabled) { + var now = EmscriptenTrace.now(); + EmscriptenTrace.post([EmscriptenTrace.EVENT_LOG_MESSAGE, now, + "MARK", Pointer_stringify(message)]); + } + if (EmscriptenTrace.googleWTFEnabled) { + window.wtf.trace.mark(Pointer_stringify(message)); + } + }, + emscripten_trace_report_error: function(error) { var now = EmscriptenTrace.now(); var callstack = (new Error).stack; @@ -155,7 +213,7 @@ var LibraryTracing = { }, emscripten_trace_record_allocation: function(address, size) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_ALLOCATE, now, address, size]); @@ -163,7 +221,7 @@ var LibraryTracing = { }, emscripten_trace_record_reallocation: function(old_address, new_address, size) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_REALLOCATE, now, old_address, new_address, size]); @@ -171,7 +229,7 @@ var LibraryTracing = { }, emscripten_trace_record_free: function(address) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_FREE, now, address]); @@ -179,21 +237,21 @@ var LibraryTracing = { }, emscripten_trace_annotate_address_type: function(address, type_name) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { EmscriptenTrace.post([EmscriptenTrace.EVENT_ANNOTATE_TYPE, address, Pointer_stringify(type_name)]); } }, emscripten_trace_associate_storage_size: function(address, size) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { EmscriptenTrace.post([EmscriptenTrace.EVENT_ASSOCIATE_STORAGE_SIZE, address, size]); } }, emscripten_trace_report_memory_layout: function() { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var memory_layout = { 'static_base': STATIC_BASE, 'static_top': STATICTOP, @@ -224,7 +282,7 @@ var LibraryTracing = { } return totalMemory; } - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var off_heap_data = { 'openal': openal_audiodata_size() } @@ -234,30 +292,39 @@ var LibraryTracing = { }, emscripten_trace_js_enter_context: function(name) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_ENTER_CONTEXT, now, name]); } + if (EmscriptenTrace.googleWTFEnabled) { + EmscriptenTrace.googleWTFEnterScope(name); + } }, emscripten_trace_enter_context: function(name) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_ENTER_CONTEXT, now, Pointer_stringify(name)]); } + if (EmscriptenTrace.googleWTFEnabled) { + EmscriptenTrace.googleWTFEnterScope(Pointer_stringify(name)); + } }, emscripten_trace_exit_context: function() { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_EXIT_CONTEXT, now]); } + if (EmscriptenTrace.googleWTFEnabled) { + EmscriptenTrace.googleWTFExitScope(); + } }, emscripten_trace_task_start: function(task_id, name) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_START, now, task_id, Pointer_stringify(name)]); @@ -265,7 +332,7 @@ var LibraryTracing = { }, emscripten_trace_task_associate_data: function(key, value) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_ASSOCIATE_DATA, Pointer_stringify(key), Pointer_stringify(value)]); @@ -273,7 +340,7 @@ var LibraryTracing = { }, emscripten_trace_task_suspend: function(explanation) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_SUSPEND, now, Pointer_stringify(explanation)]); @@ -281,7 +348,7 @@ var LibraryTracing = { }, emscripten_trace_task_resume: function(task_id, explanation) { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_RESUME, now, task_id, Pointer_stringify(explanation)]); @@ -289,15 +356,17 @@ var LibraryTracing = { }, emscripten_trace_task_end: function() { - if (EmscriptenTrace.enabled) { + if (EmscriptenTrace.postEnabled) { var now = EmscriptenTrace.now(); EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_END, now]); } }, emscripten_trace_close: function() { - EmscriptenTrace.configured = false; - EmscriptenTrace.enabled = false; + EmscriptenTrace.collectorEnabled = false; + EmscriptenTrace.googleWTFEnabled = false; + EmscriptenTrace.postEnabled = false; + EmscriptenTrace.testingEnabled = false; EmscriptenTrace.worker.postMessage({ 'cmd': 'close' }); EmscriptenTrace.worker = null; }, diff --git a/system/include/emscripten/trace.h b/system/include/emscripten/trace.h index 00f60f3e94368..3e1545c62c22b 100644 --- a/system/include/emscripten/trace.h +++ b/system/include/emscripten/trace.h @@ -12,6 +12,8 @@ extern "C" { void emscripten_trace_configure(const char *collector_url, const char *application); +void emscripten_trace_configure_for_google_wtf(void); + void emscripten_trace_configure_for_test(void); void emscripten_trace_set_enabled(bool enabled); @@ -22,6 +24,8 @@ void emscripten_trace_record_frame_start(void); void emscripten_trace_record_frame_end(void); +void emscripten_trace_mark(const char *message); + void emscripten_trace_log_message(const char *channel, const char *message); void emscripten_trace_report_error(const char *error); @@ -59,11 +63,13 @@ void emscripten_trace_close(void); #else #define emscripten_trace_configure(collector_url, application) +#define emscripten_trace_configure_for_google_wtf() #define emscripten_trace_configure_for_test() #define emscripten_trace_set_enabled(enabled) #define emscripten_trace_set_session_username(username) #define emscripten_trace_record_frame_start() #define emscripten_trace_record_frame_end() +#define emscripten_trace_mark(message) #define emscripten_trace_log_message(channel, message) #define emscripten_trace_report_error(error) #define emscripten_trace_record_allocation(address, size) From 9225eac1ea196ddb174d781bb6776805e8399324 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 11 Feb 2015 15:07:42 -0800 Subject: [PATCH 21/76] document all remaining emterpreter-async methods --- .../docs/api_reference/emscripten.h.rst | 60 ++++++++++++++++++- system/include/emscripten/emscripten.h | 10 ++-- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index 9c3208c46bfff..15ca9934e46bf 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -1077,10 +1077,10 @@ Typedefs Emterpreter-Async functions =========================== -Emterpreter-async functions are asynchronous functions that appear synchronously in C, the linker flags `-s EMTERPRETIFY -s EMTERPRETIFY_ASYNC=1` are required to use these functions. See `Emterpreter `_ for more details. +Emterpreter-async functions are asynchronous functions that appear synchronously in C, the linker flags ``-s EMTERPRETIFY -s EMTERPRETIFY_ASYNC=1`` are required to use these functions. See `Emterpreter `_ for more details. -Functions ---------- +Sleeping +-------- .. c:function:: void emscripten_sleep(unsigned int ms) @@ -1094,6 +1094,60 @@ Functions this sleep. Note that this method **does** still block the main loop, as otherwise it could recurse, if you are calling this method from it. Even so, you should use this method carefully: the order of execution is potentially very confusing this way. +Network +------- + +.. c:function:: void emscripten_wget_data(const char* url, void** pbuffer, int* pnum, int *perror); + + Synchronously fetches data off the network, and stores it to a buffer in memory, which is allocated for you. **You must free the buffer, or it will leak!** + + :param url: The URL to fetch from + :param pbuffer: An out parameter that will be filled with a pointer to a buffer containing the data that is downloaded. This space has been malloced for you, **and you must free it, or it will leak!** + :param pnum: An out parameter that will be filled with the size of the downloaded data. + :param perror: An out parameter that will be filled with a non-zero value if an error occurred. + +IndexedDB +--------- + +.. c:function:: void emscripten_idb_load(const char *db_name, const char *file_id, void** pbuffer, int* pnum, int *perror); + + Synchronously fetches data from IndexedDB, and stores it to a buffer in memory, which is allocated for you. **You must free the buffer, or it will leak!** + + :param db_name: The name of the database to load from + :param file_id: The name of the file to load + :param pbuffer: An out parameter that will be filled with a pointer to a buffer containing the data that is downloaded. This space has been malloced for you, **and you must free it, or it will leak!** + :param pnum: An out parameter that will be filled with the size of the downloaded data. + :param perror: An out parameter that will be filled with a non-zero value if an error occurred. + +.. c:function:: void emscripten_idb_store(const char *db_name, const char *file_id, void* ptr, int num, int *perror); + + Synchronously stores data to IndexedDB. + + :param db_name: The name of the database to store to + :param file_id: The name of the file to store + :param buffer: A pointer to the data to store + :param num: How many bytes to store + :param perror: An out parameter that will be filled with a non-zero value if an error occurred. + +.. c:function:: void emscripten_idb_delete(const char *db_name, const char *file_id, int *perror); + + Synchronously deletes data from IndexedDB. + + :param db_name: The name of the database to delete from + :param file_id: The name of the file to delete + :param perror: An out parameter that will be filled with a non-zero value if an error occurred. + +.. c:function:: void emscripten_idb_exists(const char *db_name, const char *file_id, int* pexists, int *perror); + + Synchronously checks if a file exists in IndexedDB. + + :param db_name: The name of the database to check in + :param file_id: The name of the file to check + :param perror: An out parameter that will be filled with a non-zero value if the file exists in that database. + :param perror: An out parameter that will be filled with a non-zero value if an error occurred. + +.. c:function:: void emscripten_idb_load(const char *db_name, const char *file_id, void** pbuffer, int* pnum, int *perror); + Asyncify functions ================== diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 8836996bb4c5b..c1dd3e52af34e 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -174,7 +174,7 @@ void emscripten_wget(const char* url, const char* file); // wget data "sync" (EMTERPRETIFY_ASYNC) -void emscripten_wget_data(const char* url, void** pbuffer, int* num, int *error); +void emscripten_wget_data(const char* url, void** pbuffer, int* pnum, int *perror); // IDB @@ -186,10 +186,10 @@ void emscripten_idb_async_exists(const char *db_name, const char *file_id, void* // IDB "sync" (EMTERPRETIFY_ASYNC) -void emscripten_idb_load(const char *db_name, const char *file_id, void** pbuffer, int* num, int *error); -void emscripten_idb_store(const char *db_name, const char *file_id, void* ptr, int num, int *error); -void emscripten_idb_delete(const char *db_name, const char *file_id, int *error); -void emscripten_idb_exists(const char *db_name, const char *file_id, int* exists, int *error); +void emscripten_idb_load(const char *db_name, const char *file_id, void** pbuffer, int* pnum, int *perror); +void emscripten_idb_store(const char *db_name, const char *file_id, void* buffer, int num, int *perror); +void emscripten_idb_delete(const char *db_name, const char *file_id, int *perror); +void emscripten_idb_exists(const char *db_name, const char *file_id, int* pexists, int *perror); // other async utilities From df8eacac2ae916eb81019e9c7525829b09b9e93a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 11 Feb 2015 10:10:07 -0800 Subject: [PATCH 22/76] fuzzing tweaks --- tests/fuzz/csmith_driver.py | 2 +- tests/fuzz/test.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py index a0f1ffb312355..264911ac760bf 100755 --- a/tests/fuzz/csmith_driver.py +++ b/tests/fuzz/csmith_driver.py @@ -159,5 +159,5 @@ def try_js(args): fails += 1 shutil.copyfile(fullname, 'newfail%d.c' % fails) continue - print '[asm.js validation ok]' + print '[asm.js validation ok in %s]' % str(engine2) diff --git a/tests/fuzz/test.sh b/tests/fuzz/test.sh index 46459c33f10cc..e931e3dd59cab 100755 --- a/tests/fuzz/test.sh +++ b/tests/fuzz/test.sh @@ -5,8 +5,8 @@ echo "builds" rm *.out *.bc *.js gcc $@ -m32 -I/home/alon/Dev/csmith/runtime -o n1.out &> /dev/null -/home/alon/Dev/fastcomp/build/Release+Asserts/bin/clang -m32 -I/home/alon/Dev/csmith/runtime -o n2.out $@ &> /dev/null -/home/alon/Dev/fastcomp/build/Release+Asserts/bin/clang -m32 -I/home/alon/Dev/csmith/runtime -emit-llvm -c -o bc.bc $@ &> o +/home/alon/Dev/fastcomp/build/Release/bin/clang -m32 -I/home/alon/Dev/csmith/runtime -o n2.out $@ &> /dev/null +/home/alon/Dev/fastcomp/build/Release/bin/clang -m32 -I/home/alon/Dev/csmith/runtime -emit-llvm -c -o bc.bc $@ &> o #EMCC_FAST_COMPILER=0 ~/Dev/emscripten/emcc $@ -I/home/alon/Dev/csmith/runtime -o js.out.js &> /dev/null #EMCC_FAST_COMPILER=0~/Dev/emscripten/emcc $@ -s UNALIGNED_MEMORY=1 -I/home/alon/Dev/csmith/runtime -o ua.out.js &> /dev/null #EMCC_FAST_COMPILER=0~/Dev/emscripten/emcc $@ -s SAFE_HEAP=1 -I/home/alon/Dev/csmith/runtime -o sh.out.js &> /dev/null From af3aad25d20ce53576261c82c98230dfbc97441e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 11 Feb 2015 15:56:27 -0800 Subject: [PATCH 23/76] add fuzzing on emterpreter --- tests/fuzz/csmith_driver.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py index 264911ac760bf..677db9d69371e 100755 --- a/tests/fuzz/csmith_driver.py +++ b/tests/fuzz/csmith_driver.py @@ -102,6 +102,12 @@ def try_js(args): shared.try_delete(filename + '.js') js_args = [shared.PYTHON, shared.EMCC, opts] + llvm_opts + [fullname, '-o', filename + '.js'] + CSMITH_CFLAGS + args + if random.random() < 0.5: + js_args += ['-s', 'EMTERPRETIFY=1'] + if random.random() < 0.5: + js_args += ['-s', 'EMTERPRETIFY_BLACKLIST=["_main"]'] + if random.random() < 0.5: + js_args += ['-s', 'EMTERPRETIFY_ASYNC=1'] print '(compile)', ' '.join(js_args) shared.check_execute(js_args) assert os.path.exists(filename + '.js') From 2b776f6970c42a75c745f70830d7c7004d23c8d3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 11 Feb 2015 16:18:20 -0800 Subject: [PATCH 24/76] fuzz combinations of emterpreted and non-emterpreted code --- tests/fuzz/csmith_driver.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py index 677db9d69371e..cf7a21e111466 100755 --- a/tests/fuzz/csmith_driver.py +++ b/tests/fuzz/csmith_driver.py @@ -105,7 +105,12 @@ def try_js(args): if random.random() < 0.5: js_args += ['-s', 'EMTERPRETIFY=1'] if random.random() < 0.5: - js_args += ['-s', 'EMTERPRETIFY_BLACKLIST=["_main"]'] + if random.random() < 0.5: + js_args += ['-s', 'EMTERPRETIFY_BLACKLIST=["_main"]'] # blacklist main and all inlined into it, but interpret the rest, tests mixing + else: + js_args += ['-s', 'EMTERPRETIFY_WHITELIST=["_main"]'] # the opposite direction + if random.random() < 0.25: + js_args += ['-s', 'INLINING_LIMIT=1'] # inline nothing, for more main->other interaction if random.random() < 0.5: js_args += ['-s', 'EMTERPRETIFY_ASYNC=1'] print '(compile)', ' '.join(js_args) From adc8ab5dc49817b475f789954612db4594d73220 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 10:39:14 -0800 Subject: [PATCH 25/76] handle dropped unary operators --- tests/test_other.py | 7 +++++++ tools/js-optimizer.js | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_other.py b/tests/test_other.py index 6a57fa42700db..461757b8933e4 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4532,6 +4532,13 @@ def get_func(src, name): } ''', [], '123\n456\n789\n159\n') + do_js_test('effectless unary', r''' +function _main() { + !(0 != 0); + !(print (123) | 0); +} +''', [], '123\n') + # codegen log tests def do_log_test(source, expected, func): diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 3f77507411540..2c407afc93af0 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6502,7 +6502,18 @@ function emterpretify(ast) { } // not a simple coercion - assert(!dropIt); + + if (dropIt) { + // a pointless thing we can drop entirely + var ret = [-1, []]; + if (hasSideEffects(node)) { + // emit it but drop the result + var child = getReg(node[2]); + releaseIfFree(child[0]); + ret[1] = child[1]; + } + return ret; + } switch (node[1]) { case '-': { From 1976f9d68ca0be4a2b78a80ec59680705cd922ab Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 11 Feb 2015 17:48:43 -0800 Subject: [PATCH 26/76] fround support in emterpreter --- emcc | 2 ++ tests/test_core.py | 3 --- tools/emterpretify.py | 19 +++++++++++--- tools/js-optimizer.js | 59 ++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/emcc b/emcc index 31cb5473545bd..57d5699554997 100755 --- a/emcc +++ b/emcc @@ -1594,6 +1594,8 @@ try: args += ['PROFILING=1'] if shared.Settings.ASSERTIONS: args += ['ASSERTIONS=1'] + if shared.Settings.PRECISE_F32: + args += ['FROUND=1'] execute(args) final = final + '.em.js' finally: diff --git a/tests/test_core.py b/tests/test_core.py index b17ef34e94b6b..e8d0429e6b201 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -482,8 +482,6 @@ def test_double_i64_conversion(self): self.do_run_from_file(src, output) def test_float32_precise(self): - if self.is_emterpreter(): return self.skip('todo') - Settings.PRECISE_F32 = 1 test_path = path_from_root('tests', 'core', 'test_float32_precise') src, output = (test_path + s for s in ('.in', '.out')) @@ -5124,7 +5122,6 @@ def test_fasta(self): for precision in [0, 1, 2]: Settings.PRECISE_F32 = precision for t in ['float', 'double']: - if self.is_emterpreter() and precision > 0: continue print precision, t src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', t) for i, j in results: diff --git a/tools/emterpretify.py b/tools/emterpretify.py index d16a4ef982f55..8482b34428fcc 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -22,15 +22,17 @@ ASSERTIONS = False PROFILING = False SWAPPABLE = False +FROUND = False def handle_arg(arg): - global ZERO, ASYNC, ASSERTIONS, PROFILING + global ZERO, ASYNC, ASSERTIONS, PROFILING, FROUND if '=' in arg: l, r = arg.split('=') if l == 'ZERO': ZERO = int(r) elif l == 'ASYNC': ASYNC = int(r) elif l == 'ASSERTIONS': ASSERTIONS = int(r) elif l == 'PROFILING': PROFILING = int(r) + elif l == 'FROUND': FROUND = int(r) return False return True @@ -42,6 +44,9 @@ def handle_arg(arg): if DEBUG: print >> sys.stderr, 'running emterpretify on', sys.argv +if FROUND: + shared.Settings.PRECISE_F32 = 1 + sys.argv = filter(handle_arg, sys.argv) # consts @@ -218,6 +223,11 @@ def handle_arg(arg): 'TSLOWD', # [lxl, lxh, ly] lx = ly (double; lx = lxl,lxh) ] +if FROUND: + OPCODES.append( + 'FROUND', # [lx, ly] lx = Math.fround(ly), rounds doubles to floats + ) + def randomize_opcodes(): global OPCODES import random @@ -255,7 +265,7 @@ def get_access(l, s='i', base='sp', offset=None): offset = '' if s == 'i': return 'HEAP32[' + str(base) + ' + (' + l + ' << 3) ' + offset + '>> 2]' - elif s == 'd': + elif s == 'd' or s == 'f': return 'HEAPF64[' + str(base) + ' + (' + l + ' << 3) ' + offset + '>> 3]' else: assert 0 @@ -266,7 +276,7 @@ def get_coerced_access(l, s='i', unsigned=False, base='sp', offset=None): return get_access(l, s, base, offset) + '|0' else: return get_access(l, s, base, offset) + '>>>0' - elif s == 'd': + elif s == 'd' or s == 'f': return '+' + get_access(l, s, base, offset) else: assert 0 @@ -420,6 +430,9 @@ def get_coerced_access(l, s='i', unsigned=False, base='sp', offset=None): CASES[ROPCODES['GETTR0']] = get_access('lx') + ' = tempRet0;' CASES[ROPCODES['SETTR0']] = 'tempRet0 = ' + get_coerced_access('lx') + ';' +if FROUND: + CASES[ROPCODES['FROUND']] = get_access('lx', s='d') + ' = Math_fround(' + get_coerced_access('ly', s='d') + ');' + # stacktop handling: if allowing async, the very bottom will contain the function being executed, # for stack trace reconstruction. We store [pc of function, curr pc] # where curr pc is the current position in that function, when asyncing diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 3f77507411540..2ec25ea77aa89 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2307,6 +2307,14 @@ function detectSign(node) { case 'conditional': case 'seq': { return detectSign(node[2]); } + case 'call': { + if (node[1][0] === 'name') { + switch (node[1][1]) { + case 'Math_fround': return ASM_NONSIGNED + default: break; + } + } + } } assert(0 , 'badd ' + JSON.stringify(node)); } @@ -6165,12 +6173,12 @@ function emterpretify(ast) { return Array.prototype.slice.call(tempUint8, 0, 8); } - function verifyCode(code) { + function verifyCode(code, stat) { if (code.length % 4 !== 0) assert(0, JSON.stringify(code)); var len = code.length; for (var i = 0; i < len; i++) { if (typeof code[i] !== 'string' && typeof code[i] !== 'number' && !(typeof code[i] === 'object' && code[i].what)) { - assert(0, i + ' : ' + JSON.stringify(code)); + assert(0, i + ' : ' + JSON.stringify(code) + ' from ' + JSON.stringify(stat)); } } } @@ -6861,6 +6869,7 @@ function emterpretify(ast) { case '>>>': opcode = 'LSHR'; tryNumAsymmetrical(true); break; default: throw 'bad ' + node[1]; } + if (!opcode) assert(0, JSON.stringify([node, type, sign])); var x, y, z; var usingNumValue = numValue !== null && ((!numValueUnsigned && ((numValue << 24 >> 24) === numValue)) || ( numValueUnsigned && ((numValue & 255) === numValue))); @@ -7113,6 +7122,13 @@ function emterpretify(ast) { assert(mul[0] === lx); return mul[1]; } + case 'Math_fround': { + assert(node[2].length === 1); + var child = getReg(node[2][0], undefined, ASM_DOUBLE, ASM_NONSIGNED, lx); + child[1].push('FROUND', lx, child[0], 0); + releaseIfFree(child[0], lx); + return child[1]; + } } if ((target in EMTERPRETED_FUNCS) && !PROFILING) internal = true; } else { @@ -7167,7 +7183,7 @@ function emterpretify(ast) { if (freeLocals.length !== before) assert(0, [before, freeLocals.length] + ' due to ' + astToSrc(stat)); // the statement is done - nothing should still be held on to var curr = raw[1]; //printErr('stat: ' + JSON.stringify(curr)); - verifyCode(curr); + verifyCode(curr, stat); ret = ret.concat(curr); }); return ret; @@ -7455,6 +7471,39 @@ function emterpretify(ast) { //printErr('emterpretifying ' + func[1]); + // we implement floats as doubles, and just decrease precision when fround is called. flip floats to doubles, but we + // must restore this at the end when we emit the trampolines + var trueParams = asmData.params; + asmData.params = {}; + for (var t in trueParams) { + if (trueParams[t] === ASM_FLOAT) { + asmData.params[t] = ASM_DOUBLE; + } else { + asmData.params[t] = trueParams[t]; + } + } + var trueVars = asmData.vars; + asmData.vars = {}; + for (var t in trueVars) { + if (trueVars[t] === ASM_FLOAT) { + asmData.vars[t] = ASM_DOUBLE; + } else { + asmData.vars[t] = trueVars[t]; + } + } + traverse(func, function() {} , function(node, type) { + // Math_fround(x) => +Math_fround(+x), so that see no float types on temp values; types are double or int, and fround is just a function we emit + if (type === 'call' && node[1][0] === 'name' && node[1][1] === 'Math_fround') { + assert(node[2].length === 1); + old = ['call', node[1], [['unary-prefix', '+', node[2][0]]]]; + node[0] = 'unary-prefix'; + node[1] = '+'; + node[2] = old; + } + }); + + // consider locals + var locals = {}; var numLocals = 0; // ignores slow locals, they are over 255 and not directly accessible @@ -7546,6 +7595,7 @@ function emterpretify(ast) { if ((func[1] in EXTERNAL_EMTERPRETED_FUNCS) || PROFILING) { // this is reachable from outside emterpreter code, set up a trampoline + asmData.params = trueParams; // restore them, we altered float=>double asmData.vars = {}; if (zero && !onlyLeavesAreZero) { // emterpreters run using the stack starting at 0. we must copy it so we can restore it later @@ -7564,8 +7614,8 @@ function emterpretify(ast) { var code; switch (asmData.params[arg]) { case ASM_INT: code = 'HEAP32[' + (zero ? (bump >> 2) : ('EMTSTACKTOP + ' + bump + ' >> 2')) + '] = ' + arg + ';'; break; + case ASM_FLOAT: case ASM_DOUBLE: code = 'HEAPF64[' + (zero ? (bump >> 3) : ('EMTSTACKTOP + ' + bump + ' >> 3')) + '] = ' + arg + ';'; break; - case ASM_FLOAT: code = 'HEAPF32[' + (zero ? (bump >> 2) : ('EMTSTACKTOP + ' + bump + ' >> 2')) + '] = ' + arg + ';'; break; default: throw 'bad'; } argStats.push(srcToStat(code)); @@ -7594,6 +7644,7 @@ function emterpretify(ast) { var ret; switch (asmData.ret) { case ASM_INT: ret = srcToExp('HEAP32[EMTSTACKTOP >> 2]'); break; + case ASM_FLOAT: case ASM_DOUBLE: ret = srcToExp('HEAPF64[EMTSTACKTOP >> 3]'); break; default: throw 'bad'; } From f1f1643b8ed8f6d18799cd205af12dc7bdc614bb Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 14:58:37 -0800 Subject: [PATCH 27/76] ignore debugger keyword in emterpreter --- tests/test_core.py | 1 - tools/js-optimizer.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index e8d0429e6b201..775ff159f1520 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2251,7 +2251,6 @@ def test_inlinejs2(self): self.do_run_from_file(src, output) def test_inlinejs3(self): - if self.is_emterpreter(): return self.skip('debugger keyword is meaningless in emterpreter') test_path = path_from_root('tests', 'core', 'test_inlinejs3') src, output = (test_path + s for s in ('.in', '.out')) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 2ec25ea77aa89..0cc98f666d40c 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6289,6 +6289,7 @@ function emterpretify(ast) { } case 'inf': return makeNum(Infinity, ASM_DOUBLE); case 'nan': return makeNum(NaN, ASM_DOUBLE); + case 'debugger': return [-1, []]; // nothing to do here (should we?) default: { var x = getFree(); // we assert in the python driver that these are ints From 77732f612b85eb39a594ac37d42621f308f38c60 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 14:59:27 -0800 Subject: [PATCH 28/76] enable test_memorygrowth in emterpreter --- tests/test_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 775ff159f1520..cdd8755f885f1 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2257,7 +2257,6 @@ def test_inlinejs3(self): self.do_run_from_file(src, output) def test_memorygrowth(self): - if self.is_emterpreter(): return self.skip('todo') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('memory growth is only supported with typed arrays mode 2') self.banned_js_engines = [V8_ENGINE] # stderr printing limitations in v8 From 032008a641d3678537a586d2fdb867ca6cd24f85 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 15:01:58 -0800 Subject: [PATCH 29/76] more emterpreter coverage on cubescript --- tests/test_core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index cdd8755f885f1..cbff725a9ef11 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5250,8 +5250,6 @@ 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 - if self.is_emterpreter(): - self.emcc_args += ['-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'ASSERTIONS=1'] # 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... @@ -5274,6 +5272,11 @@ def test_cubescript(self): main = main[:main.find('\n}')] assert main.count('\n') <= 7, ('must not emit too many postSets: %d' % main.count('\n')) + ' : ' + main + if self.is_emterpreter(): + print 'emterpreter/async/assertions' # extra coverage + self.emcc_args += ['-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'ASSERTIONS=1'] + self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') + # Tests the full SSE1 API. def test_sse1(self): return self.skip('TODO: This test fails due to bugs #2840, #3044, #3045, #3046 and #3048 (also see #3043 and #3049)') From 360d23aa8c215de5afbef707dff9e051e41d7a23 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 15:35:15 -0800 Subject: [PATCH 30/76] only emit used opcodes in emterpreter --- tools/emterpretify.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 8482b34428fcc..f2fc42834bda0 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -508,6 +508,10 @@ def handle_async_post_call(): CASES[ROPCODES['TSLOWD']] = get_access('inst >>> 16', s='d') + ' = ' + get_coerced_access('lx', s='d') + ';' +opcode_used = {} +for opcode in OPCODES: + opcode_used[opcode] = False + def make_emterpreter(zero=False): # return is specialized per interpreter CASES[ROPCODES['RET']] = pop_stacktop(zero) @@ -600,7 +604,7 @@ def process(code): %s default: assert(0); } -''' % ('\n'.join([fix_case(' case %d: %s break;' % (k, CASES[k])) for k in sorted(CASES.keys())])) +''' % ('\n'.join([fix_case(' case %d: %s break;' % (k, CASES[k])) for k in sorted(CASES.keys()) if opcode_used[OPCODES[k]]])) else: # emit an inner interpreter (innerterpreter) loop, of trivial opcodes that hopefully the JS engine will implement with no spills assert OPCODES[-1] == 'FUNC' # we don't need to emit that one @@ -895,6 +899,7 @@ def post_process_code(code): for i in range(len(code)/4): j = i*4 if type(code[j]) in (str, unicode): + opcode_used[code[j]] = True code[j] = ROPCODES[code[j]] # sanity checks From a331bd364b150078c0b114d922774daa355a6e17 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 16:28:23 -0800 Subject: [PATCH 31/76] support asm-global doubles in emterpreter --- tools/emterpretify.py | 47 +++++++++++++++++++++++++++---------------- tools/js-optimizer.js | 6 +++--- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index f2fc42834bda0..1b11570a65bf3 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -199,8 +199,9 @@ def handle_arg(arg): 'GETTR0', # [l, 0, 0] l = tempRet0 'SETTR0', # [l, 0, 0] tempRet0 = l 'GETGLBI', # [l, vl, vh] get global value, int, indexed by v - #'GETGLBD', # [l, vl, vh] get global value, double, indexed by v + 'GETGLBD', # [l, vl, vh] get global value, double, indexed by v 'SETGLBI', # [vl, vh, l] set global value, int, indexed by v (v = l) + 'SETGLBD', # [vl, vh, l] set global value, double, indexed by v (v = l) 'INTCALL', # [lx, 0, 0] [target] [params] (lx = ) target(params..) # Internal, emterpreter-to-emterpreter call. @@ -563,22 +564,31 @@ def make_target_call(i): '\n }' if ROPCODES['GETGLBI'] not in CASES: - def make_load(i): - sig = 'i' + def make_load(i, t): name = rglobal_vars[i] - return ' ' + get_access('lx', sig[0]) + ' = ' + name + '; PROCEED_WITH_PC_BUMP;' - CASES[ROPCODES['GETGLBI']] = 'switch (ly|0) {\n' + \ - '\n'.join([' case %d: {\n%s\n }' % (i, make_load(i)) for i in range(global_var_id)]) + \ - '\n default: assert(0);' + \ - '\n }' - def make_store(i): - sig = 'i' + return ' ' + get_access('lx', t) + ' = ' + name + '; PROCEED_WITH_PC_BUMP;' + + def make_getglb(suffix, t): + CASES[ROPCODES['GETGLB' + suffix]] = 'switch (ly|0) {\n' + \ + '\n'.join([' case %d: {\n%s\n }' % (i, make_load(i, t)) for i in range(global_var_id)]) + \ + '\n default: assert(0);' + \ + '\n }' + + make_getglb('I', 'i') + make_getglb('D', 'd') + + def make_store(i, t): name = rglobal_vars[i] - return ' ' + name + ' = ' + get_coerced_access('lz', sig[0]) + '; PROCEED_WITH_PC_BUMP;' - CASES[ROPCODES['SETGLBI']] = 'switch ((inst >> 8)&255) {\n' + \ - '\n'.join([' case %d: {\n%s\n }' % (i, make_store(i)) for i in range(global_var_id)]) + \ - '\n default: assert(0);' + \ - '\n }' + return ' ' + name + ' = ' + get_coerced_access('lz', t) + '; PROCEED_WITH_PC_BUMP;' + + def make_setglb(suffix, t): + CASES[ROPCODES['SETGLB' + suffix]] = 'switch ((inst >> 8)&255) {\n' + \ + '\n'.join([' case %d: {\n%s\n }' % (i, make_store(i, t)) for i in range(global_var_id)]) + \ + '\n default: assert(0);' + \ + '\n }' + + make_setglb('I', 'i') + make_setglb('D', 'd') def fix_case(case): # we increment pc at the top of the loop. to avoid a pc bump, we decrement it first; this is rare, most opcodes just continue; this avoids any code at the end of the loop @@ -823,13 +833,16 @@ def process_code(func, code, absolute_targets): # fix global-accessing instructions' targets target = code[j+2] imp = asm.imports[target] - assert '|0' in imp or '| 0' in imp or imp == '0' + if code[j] == 'GETGLBI' and not ('|0' in imp or '| 0' in imp or imp == '0'): + # the js optimizer doesn't know all types, we must fix it up here + assert '.0' in imp or '+' in imp + code[j] = 'GETGLBD' if target not in global_vars: global_vars[target] = global_var_id rglobal_vars[global_var_id] = target global_var_id += 1 code[j+2] = global_vars[target] - elif code[j] in ['SETGLBI']: + elif code[j] in ['SETGLBI', 'SETGLBD']: # fix global-accessing instructions' targets target = code[j+1] if target not in global_vars: diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 0cc98f666d40c..dd8de7452a4a1 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6292,7 +6292,7 @@ function emterpretify(ast) { case 'debugger': return [-1, []]; // nothing to do here (should we?) default: { var x = getFree(); - // we assert in the python driver that these are ints + // We actually do not know the type here, and even hints won't help for global1 = global2. We swap GETGLBI to D in emterpretify.py as needed. return [x, ['GETGLBI', x, name, 0]]; } } @@ -6345,8 +6345,8 @@ function emterpretify(ast) { case 'tempRet0': opcode = 'SETTR0'; break; default: { var type = detectType(value, asmData); - assert(type === ASM_INT); - reg[1].push('SETGLBI', name, 0, reg[0]); + assert(type === ASM_INT || type === ASM_DOUBLE); + reg[1].push(type === ASM_INT ? 'SETGLBI' : 'SETGLBD', name, 0, reg[0]); return reg; // caller will free reg[0] if necessary } } From ca0ffadbbcf9a5060be199eb5223d5dfe1a92d5d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 16:43:35 -0800 Subject: [PATCH 32/76] enable a limited version of test_emscripten_log in emterpreter --- tests/emscripten_log/emscripten_log.cpp | 5 +++++ tests/test_core.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/emscripten_log/emscripten_log.cpp b/tests/emscripten_log/emscripten_log.cpp index f4cc41db88589..1a2f3c23c8a79 100644 --- a/tests/emscripten_log/emscripten_log.cpp +++ b/tests/emscripten_log/emscripten_log.cpp @@ -99,8 +99,13 @@ void __attribute__((noinline)) bar(int = 0, char * = 0, double = 0) // Arbitrary char *buffer = new char[21]; buffer[20] = 0x01; // Magic sentinel that should not change its value. emscripten_get_callstack(EM_LOG_C_STACK | EM_LOG_DEMANGLE | EM_LOG_NO_PATHS | EM_LOG_FUNC_PARAMS, buffer, 20); +#if EMTERPRETER + MYASSERT(!!strstr(buffer, "at emterpret ("), "Truncated emterpreter callstack was %s!", buffer); + MYASSERT(buffer[20] == 0x01); +#else MYASSERT(!!strstr(buffer, "at bar(int,"), "Truncated callstack was %s!", buffer); MYASSERT(buffer[20] == 0x01); +#endif delete[] buffer; /* With EM_LOG_JS_STACK, the callstack will be diff --git a/tests/test_core.py b/tests/test_core.py index cbff725a9ef11..4d094092aab50 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6961,7 +6961,9 @@ def post(filename): self.build(src, dirname, os.path.join(dirname, 'src.cpp'), post_build=(None, post)) def test_emscripten_log(self): - if self.is_emterpreter(): return self.skip('todo') + if self.is_emterpreter(): + self.emcc_args += ['--profiling-funcs'] # without this, stack traces are not useful (we jump emterpret=>emterpret) + Building.COMPILER_TEST_OPTS += ['-DEMTERPRETER'] # even so, we get extra emterpret() calls on the stack 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] From 6dc742716ef0249ea22f5092ed9e9d9e1b16e715 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 17:10:57 -0800 Subject: [PATCH 33/76] fix parsing of |var a = 0, b = 0| in asm_module.py --- tools/asm_module.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tools/asm_module.py b/tools/asm_module.py index fcf86ae5509d6..1f716b2597f0c 100644 --- a/tools/asm_module.py +++ b/tools/asm_module.py @@ -43,9 +43,18 @@ def __init__(self, filename): self.pre_imports_js = self.js[self.start_asm:first_var] self.imports_js = self.js[first_var:self.start_funcs] self.imports = {} - for imp in js_optimizer.import_sig.finditer(self.imports_js): - key, value = imp.group(0).split('var ')[1][:-1].split('=', 1) - self.imports[key.strip()] = value.strip() + for i in js_optimizer.import_sig.finditer(self.imports_js): + imp = i.group(0).split('var ')[1][:-1] + if ',' not in imp: + key, value = imp.split('=', 1) + self.imports[key.strip()] = value.strip() + else: + for part in imp.split(','): + assert part.count('(') == part.count(')') # we must not break ',' in func(x, y)! + assert part.count('=') == 1 + key, value = part.split('=') + self.imports[key.strip()] = value.strip() + #print >> sys.stderr, 'imports', self.imports # funcs From bd54286ed5d0e1cd5af93430dfa7bb4044c79b86 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Feb 2015 18:10:49 -0800 Subject: [PATCH 34/76] update other.test_emterpreter --- tests/test_other.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_other.py b/tests/test_other.py index 6a57fa42700db..54c88d8b2eea1 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4390,7 +4390,7 @@ def do_js_test(name, source, args, output): normal = open('a.out.js').read() do_emcc_test('fannkuch.cpp', ['5'], 'Pfannkuchen(5) = 7.', ['-g2', '--profiling']) profiling = open('a.out.js').read() - assert len(profiling) > len(normal) + 1000, [len(profiling), len(normal)] # should be much larger + assert len(profiling) > len(normal) + 500, [len(profiling), len(normal)] # should be much larger print 'blacklisting' From 6dfb5f6fcabcd509f3f0652499c746725bb574b1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 13 Feb 2015 11:50:42 -0800 Subject: [PATCH 35/76] refactor global handling code in emterpreter in preparation for having globals with different types --- tools/asm_module.py | 8 ++++++++ tools/emterpretify.py | 35 +++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/tools/asm_module.py b/tools/asm_module.py index 1f716b2597f0c..8a5ccf56b5490 100644 --- a/tools/asm_module.py +++ b/tools/asm_module.py @@ -315,3 +315,11 @@ def apply_funcs_map(self, funcs_map): # assumes self.funcs is the set of funcs, jses.append(funcs_map[f]) self.funcs_js = '\n'.join(jses) + def get_import_type(self, imp): + if '|0' in imp or '| 0' in imp or imp == '0': + return 'i' + elif '.0' in imp or '+' in imp: + return 'd' + else: + return '?' + diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 1b11570a65bf3..c371d2b3a9414 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -799,12 +799,30 @@ def process(code): global_vars = {} rglobal_vars = {} + global_var_types = {} global_var_id = 0 + def note_global(target, j, code): + global global_var_id + imp = asm.imports[target] + ty = asm.get_import_type(imp) + assert ty in ['i', 'd'] + if code[j] == 'GETGLBI' and ty == 'd': + # the js optimizer doesn't know all types, we must fix it up here + assert '.0' in imp or '+' in imp, imp + code[j] = 'GETGLBD' + ty = 'd' + if target not in global_vars: + global_vars[target] = global_var_id + rglobal_vars[global_var_id] = target + global_var_id += 1 + global_var_types[target] = ty + else: + assert global_var_types[target] == ty + call_sigs = {} # signatures appearing for each call target def process_code(func, code, absolute_targets): global global_func_id - global global_var_id absolute_start = code_start + len(all_code) # true absolute starting point of this function #print 'processing code', func, absolute_start for i in range(len(code)/4): @@ -832,23 +850,12 @@ def process_code(func, code, absolute_targets): elif code[j] in ['GETGLBI', 'GETGLBD']: # fix global-accessing instructions' targets target = code[j+2] - imp = asm.imports[target] - if code[j] == 'GETGLBI' and not ('|0' in imp or '| 0' in imp or imp == '0'): - # the js optimizer doesn't know all types, we must fix it up here - assert '.0' in imp or '+' in imp - code[j] = 'GETGLBD' - if target not in global_vars: - global_vars[target] = global_var_id - rglobal_vars[global_var_id] = target - global_var_id += 1 + note_global(target, j, code) code[j+2] = global_vars[target] elif code[j] in ['SETGLBI', 'SETGLBD']: # fix global-accessing instructions' targets target = code[j+1] - if target not in global_vars: - global_vars[target] = global_var_id - rglobal_vars[global_var_id] = target - global_var_id += 1 + note_global(target, j, code) code[j+1] = global_vars[target] elif code[j] == 'absolute-value': # put the 32-bit absolute value of an abolute target here From 61d9ef4371f8b936262d97965b23656be55954af Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 13 Feb 2015 13:43:42 -0800 Subject: [PATCH 36/76] emit *ETGLB* global vars for the appropriate type opcode --- tools/emterpretify.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index c371d2b3a9414..b6da36f538d31 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -570,7 +570,7 @@ def make_load(i, t): def make_getglb(suffix, t): CASES[ROPCODES['GETGLB' + suffix]] = 'switch (ly|0) {\n' + \ - '\n'.join([' case %d: {\n%s\n }' % (i, make_load(i, t)) for i in range(global_var_id)]) + \ + '\n'.join([' case %d: {\n%s\n }' % (i, make_load(i, t)) for i in range(global_var_id) if global_var_types[rglobal_vars[i]] == t]) + \ '\n default: assert(0);' + \ '\n }' @@ -583,7 +583,7 @@ def make_store(i, t): def make_setglb(suffix, t): CASES[ROPCODES['SETGLB' + suffix]] = 'switch ((inst >> 8)&255) {\n' + \ - '\n'.join([' case %d: {\n%s\n }' % (i, make_store(i, t)) for i in range(global_var_id)]) + \ + '\n'.join([' case %d: {\n%s\n }' % (i, make_store(i, t)) for i in range(global_var_id) if global_var_types[rglobal_vars[i]] == t]) + \ '\n default: assert(0);' + \ '\n }' From 6e3dff168a293e1048c20426d8e83f145a7f6fd5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 13 Feb 2015 13:44:40 -0800 Subject: [PATCH 37/76] handle labeled blocks in emterpretify --- tools/js-optimizer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index dd8de7452a4a1..bc4da94f62103 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6567,6 +6567,8 @@ function emterpretify(ast) { return makeWhile(inner, name); } else if (inner[0] === 'switch') { return makeSwitch(inner, name); + } else if (inner[0] === 'block') { + return makeDo(['do', ['num', 0], inner], name); } throw 'sigh ' + inner[0]; } From 6532ca5d257d471af9b0df837b8b07a1dcdf6d01 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 13 Feb 2015 15:35:43 -0800 Subject: [PATCH 38/76] handle switches with no non-default cases, in emterpreter --- tools/js-optimizer.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index bc4da94f62103..c0192771e12e0 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -7013,6 +7013,20 @@ function emterpretify(ast) { } function makeSwitch(node, label) { + var cases = node[2]; + // there must be one non-default case, otherwise we add a fake one + var normals = 0; + for (var i = 0; i < cases.length; i++) { + var c = cases[i]; + var id = getId(c[0]); + if (id !== 'default') normals++; + } + if (normals === 0) { + // ignore the condition, we must always reach the default + node[1] = ['seq', node[1], ['num', 0]]; // always 0 + cases.unshift([['num', 1], ['block', []]]); // a block for 1, which is never hit + } + // now the switch is normalized and has one non-default case, proceed var condition = getReg(node[1]); var exit = getMarker('switch-exit'); // parse cases and emit code @@ -7022,7 +7036,6 @@ function emterpretify(ast) { breakLabels[label] = exit; } var data = {}; - var cases = node[2]; var minn = Infinity, maxx = -Infinity; function getId(raw) { if (raw === null) return 'default'; From 21097a23f792a718d6df7411e9553b4e21fee5b7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 13 Feb 2015 15:55:55 -0800 Subject: [PATCH 39/76] enable emterpreter on freetype-outlining test --- tests/test_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 4d094092aab50..a734e70b17bec 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5461,7 +5461,6 @@ def process(filename): # Main for outlining in [0, 5000]: - if outlining and self.is_emterpreter(): continue Settings.OUTLINING_LIMIT = outlining print >> sys.stderr, 'outlining:', outlining self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(), From b894b84369820bc88a9f96b4a05fa15c6db6318c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 13 Feb 2015 17:18:16 -0800 Subject: [PATCH 40/76] option to have a network request already in motion for memory init file; #3187 --- src/postamble.js | 31 +++++++++++++++++++++++++++---- tests/mem_init_request.cpp | 19 +++++++++++++++++++ tests/test_browser.py | 25 +++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 tests/mem_init_request.cpp diff --git a/src/postamble.js b/src/postamble.js index 6f78f0a6f1ea7..c389bc9756957 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -16,7 +16,8 @@ if (memoryInitializer) { #endif } else { addRunDependency('memory initializer'); - Browser.asyncLoad(memoryInitializer, function(data) { + function applyMemoryInitializer(data) { + if (data.byteLength) data = new Uint8Array(data); #if USE_TYPED_ARRAYS == 2 #if ASSERTIONS for (var i = 0; i < data.length; i++) { @@ -28,9 +29,31 @@ if (memoryInitializer) { allocate(data, 'i8', ALLOC_NONE, STATIC_BASE); #endif removeRunDependency('memory initializer'); - }, function(data) { - throw 'could not load memory initializer ' + memoryInitializer; - }); + } + var request = Module['memoryInitializerRequest']; + if (request) { + // a network request has already been created, just use that + if (request.response) { + setTimeout(function() { + applyMemoryInitializer(request.response); + }, 0); // it's already here; but, apply it asynchronously + } else { + request.addEventListener('load', function() { // wait for it + if (request.status !== 200 && request.status !== 0) { + console.warn('a problem seems to have happened with Module.memoryInitializerRequest, status: ' + request.status); + } + if (!request.response || typeof request.response !== 'object' || !request.response.byteLength) { + console.warn('a problem seems to have happened with Module.memoryInitializerRequest response (expected ArrayBuffer): ' + request.response); + } + applyMemoryInitializer(request.response); + }); + } + } else { + // fetch it from the network ourselves + Browser.asyncLoad(memoryInitializer, applyMemoryInitializer, function() { + throw 'could not load memory initializer ' + memoryInitializer; + }); + } } } diff --git a/tests/mem_init_request.cpp b/tests/mem_init_request.cpp new file mode 100644 index 0000000000000..369b3d6e34f12 --- /dev/null +++ b/tests/mem_init_request.cpp @@ -0,0 +1,19 @@ +#include +#include + +int main() { + const char *hello = "hello world"; + int result = 1; + putc('g', stdout); + putc('o', stdout); + putc('!', stdout); + if (strchr(hello, 'l') != hello + 2) result = 0; + if (strchr(hello, 'w') != hello + 6) result = 0; + putc('d', stdout); + putc('o', stdout); + putc('n', stdout); + putc('e', stdout); + REPORT_RESULT(); + return 0; +} + diff --git a/tests/test_browser.py b/tests/test_browser.py index 25fa2aded5440..9c9030d099e03 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1801,6 +1801,31 @@ def test_mem_init(self): # otherwise, we just overwrite self.btest('mem_init.cpp', expected='3', args=['--pre-js', 'pre.js', '--post-js', 'post.js', '--memory-init-file', '1', '-s', 'ASSERTIONS=0']) + def test_mem_init_request(self): + def test(what, status): + print what, status + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var xhr = Module.memoryInitializerRequest = new XMLHttpRequest(); + xhr.open('GET', "''' + what + '''", true); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + + window.onerror = function() { + Module.print('fail!'); + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://localhost:8888/report_result?0'); + xhr.onload = function() { + console.log('close!'); + window.close(); + }; + xhr.send(); + }; + ''') + self.btest('mem_init_request.cpp', expected=status, args=['--pre-js', 'pre.js', '--memory-init-file', '1']) + + test('test.html.mem', '1') + test('nothing.nowhere', '0') + def test_runtime_misuse(self): post_prep = ''' var expected_ok = false; From fc70c1808ca55ef797e12e908c2a1d3605e1b86c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 13 Feb 2015 18:16:50 -0800 Subject: [PATCH 41/76] document Module.memoryInitializerRequest ; #3187 --- site/source/docs/tools_reference/emcc.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/source/docs/tools_reference/emcc.rst b/site/source/docs/tools_reference/emcc.rst index 3ddaa1b2729dd..7494035452516 100644 --- a/site/source/docs/tools_reference/emcc.rst +++ b/site/source/docs/tools_reference/emcc.rst @@ -397,6 +397,9 @@ Options that are modified or new in *emcc* are listed below: - ``1``: Emit a separate memory initialization file in binary format. This is more efficient than storing it as text inside JavaScript, but does mean you have another file to publish. The binary file will also be loaded asynchronously, which means ``main()`` will not be called until the file is downloaded and applied; you cannot call any C functions until it arrives. This is the default setting when compiling with -O2 or higher. .. note:: The :ref:`safest way ` to ensure that it is safe to call C functions (the initialisation file has loaded) is to call a notifier function from ``main()``. + + .. note:: If you assign a network request to ``Module.memoryInitializerRequest`` (before the script runs), then it will use that request instead of automatically starting a download for you. This is beneficial in that you can, in your HTML, fire off a request for the memory init file before the script actually arrives. For this to work, the network request should be an XMLHttpRequest with responseType set to ``'arraybuffer'``. (You can also put any other object here, all it must provide is a ``.response`` property containing an ArrayBuffer.) + ``-Wno-warn-absolute-paths`` Suppress warnings about the use of absolute paths in ``-I`` and ``-L`` command line directives. This is used to hide the warnings and acknowledge that the explicit use of absolute paths is intentional. From 3d9849f8cb3c72749c9e4534ec42fa89e1f5f9d9 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 14 Feb 2015 10:12:27 -0800 Subject: [PATCH 42/76] Use the new check function for SIMD casts. --- emscripten.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emscripten.py b/emscripten.py index a132ece77c0fc..1c4a4ba687735 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1093,7 +1093,7 @@ def make_emulated_param(i): simdfloattypes = ['float32x4'] simdinttypes = ['int32x4'] simdtypes = simdfloattypes + simdinttypes - simdfuncs = ['add', 'sub', 'neg', 'mul', + simdfuncs = ['check', 'add', 'sub', 'neg', 'mul', 'equal', 'lessThan', 'greaterThan', 'notEqual', 'lessThanOrEqual', 'greaterThanOrEqual', 'select', 'and', 'or', 'xor', 'not', From 43333870654b3486c9db788c5e12f6b5b751fd54 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 14 Feb 2015 10:19:01 -0800 Subject: [PATCH 43/76] Update ecmascript_simd.js to the latest upstream --- src/ecmascript_simd.js | 719 +++++++++++++++++++++++------------------ 1 file changed, 397 insertions(+), 322 deletions(-) diff --git a/src/ecmascript_simd.js b/src/ecmascript_simd.js index ddd613d3e2d89..75ec3c241abc7 100644 --- a/src/ecmascript_simd.js +++ b/src/ecmascript_simd.js @@ -94,13 +94,13 @@ _SIMD_PRIVATE.frombool = function(x) { // Save/Restore utilities for implementing bitwise conversions. _SIMD_PRIVATE.saveFloat64x2 = function(x) { - x = SIMD.float64x2(x); + x = SIMD.float64x2.check(x); _SIMD_PRIVATE._f64x2[0] = x.x; _SIMD_PRIVATE._f64x2[1] = x.y; } _SIMD_PRIVATE.saveFloat32x4 = function(x) { - x = SIMD.float32x4(x); + x = SIMD.float32x4.check(x); _SIMD_PRIVATE._f32x4[0] = x.x; _SIMD_PRIVATE._f32x4[1] = x.y; _SIMD_PRIVATE._f32x4[2] = x.z; @@ -108,7 +108,7 @@ _SIMD_PRIVATE.saveFloat32x4 = function(x) { } _SIMD_PRIVATE.saveInt32x4 = function(x) { - x = SIMD.int32x4(x); + x = SIMD.int32x4.check(x); _SIMD_PRIVATE._i32x4[0] = x.x; _SIMD_PRIVATE._i32x4[1] = x.y; _SIMD_PRIVATE._i32x4[2] = x.z; @@ -116,7 +116,7 @@ _SIMD_PRIVATE.saveInt32x4 = function(x) { } _SIMD_PRIVATE.saveInt16x8 = function(x) { - x = SIMD.int16x8(x); + x = SIMD.int16x8.check(x); _SIMD_PRIVATE._i16x8[0] = x.s0; _SIMD_PRIVATE._i16x8[1] = x.s1; _SIMD_PRIVATE._i16x8[2] = x.s2; @@ -128,7 +128,7 @@ _SIMD_PRIVATE.saveInt16x8 = function(x) { } _SIMD_PRIVATE.saveInt8x16 = function(x) { - x = SIMD.int8x16(x); + x = SIMD.int8x16.check(x); _SIMD_PRIVATE._i8x16[0] = x.s0; _SIMD_PRIVATE._i8x16[1] = x.s1; _SIMD_PRIVATE._i8x16[2] = x.s2; @@ -186,13 +186,6 @@ if (typeof SIMD.float32x4 === "undefined") { * @constructor */ SIMD.float32x4 = function(x, y, z, w) { - if (arguments.length == 1) { - if (!(x instanceof SIMD.float32x4)) { - throw new TypeError("argument is not a float32x4."); - } - return x; - } - if (!(this instanceof SIMD.float32x4)) { return new SIMD.float32x4(x, y, z, w); } @@ -233,6 +226,20 @@ if (typeof SIMD.float32x4 === "undefined") { }); } +if (typeof SIMD.float32x4.check === "undefined") { + /** + * Check whether the argument is a float32x4. + * @param {float32x4} v An instance of float32x4. + * @return {float32x4} The float32x4 instance. + */ + SIMD.float32x4.check = function(v) { + if (!(v instanceof SIMD.float32x4)) { + throw new TypeError("argument is not a float32x4."); + } + return v; + } +} + if (typeof SIMD.float32x4.splat === "undefined") { /** * Construct a new instance of float32x4 number with the same value @@ -251,7 +258,7 @@ if (typeof SIMD.float32x4.fromFloat64x2 === "undefined") { * @return {float32x4} A float32x4 with .x and .y from t */ SIMD.float32x4.fromFloat64x2 = function(t) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float32x4(t.x, t.y, 0, 0); } } @@ -262,7 +269,7 @@ if (typeof SIMD.float32x4.fromInt32x4 === "undefined") { * @return {float32x4} An integer to float conversion copy of t. */ SIMD.float32x4.fromInt32x4 = function(t) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.float32x4(t.x, t.y, t.z, t.w); } } @@ -319,13 +326,6 @@ if (typeof SIMD.float64x2 === "undefined") { * @constructor */ SIMD.float64x2 = function(x, y) { - if (arguments.length == 1) { - if (!(x instanceof SIMD.float64x2)) { - throw new TypeError("argument is not a float64x2."); - } - return x; - } - if (!(this instanceof SIMD.float64x2)) { return new SIMD.float64x2(x, y); } @@ -355,6 +355,20 @@ if (typeof SIMD.float64x2 === "undefined") { }); } +if (typeof SIMD.float64x2.check === "undefined") { + /** + * Check whether the argument is a float64x2. + * @param {float64x2} v An instance of float64x2. + * @return {float64x2} The float64x2 instance. + */ + SIMD.float64x2.check = function(v) { + if (!(v instanceof SIMD.float64x2)) { + throw new TypeError("argument is not a float64x2."); + } + return v; + } +} + if (typeof SIMD.float64x2.splat === "undefined") { /** * Construct a new instance of float64x2 number with the same value @@ -373,7 +387,7 @@ if (typeof SIMD.float64x2.fromFloat32x4 === "undefined") { * @return {float64x2} A float64x2 with .x and .y from t */ SIMD.float64x2.fromFloat32x4 = function(t) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float64x2(t.x, t.y); } } @@ -384,7 +398,7 @@ if (typeof SIMD.float64x2.fromInt32x4 === "undefined") { * @return {float64x2} A float64x2 with .x and .y from t */ SIMD.float64x2.fromInt32x4 = function(t) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.float64x2(t.x, t.y); } } @@ -443,13 +457,6 @@ if (typeof SIMD.int32x4 === "undefined") { * @constructor */ SIMD.int32x4 = function(x, y, z, w) { - if (arguments.length == 1) { - if (!(x instanceof SIMD.int32x4)) { - throw new TypeError("argument is not a int32x4."); - } - return x; - } - if (!(this instanceof SIMD.int32x4)) { return new SIMD.int32x4(x, y, z, w); } @@ -506,6 +513,20 @@ if (typeof SIMD.int32x4 === "undefined") { }); } +if (typeof SIMD.int32x4.check === "undefined") { + /** + * Check whether the argument is a int32x4. + * @param {int32x4} v An instance of int32x4. + * @return {int32x4} The int32x4 instance. + */ + SIMD.int32x4.check = function(v) { + if (!(v instanceof SIMD.int32x4)) { + throw new TypeError("argument is not a int32x4."); + } + return v; + } +} + if (typeof SIMD.int32x4.bool === "undefined") { /** * Construct a new instance of int32x4 number with either true or false in each @@ -542,7 +563,7 @@ if (typeof SIMD.int32x4.fromFloat32x4 === "undefined") { * @return {int32x4} with a integer to float conversion of t. */ SIMD.int32x4.fromFloat32x4 = function(t) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.int32x4(t.x, t.y, t.z, t.w); } } @@ -553,7 +574,7 @@ if (typeof SIMD.int32x4.fromFloat64x2 === "undefined") { * @return {int32x4} An int32x4 with .x and .y from t */ SIMD.int32x4.fromFloat64x2 = function(t) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.int32x4(t.x, t.y, 0, 0); } } @@ -616,13 +637,6 @@ if (typeof SIMD.int16x8 === "undefined") { * @constructor */ SIMD.int16x8 = function(s0, s1, s2, s3, s4, s5, s6, s7) { - if (arguments.length == 1) { - if (!(s0 instanceof SIMD.int16x8)) { - throw new TypeError("argument is not a int16x8."); - } - return s0; - } - if (!(this instanceof SIMD.int16x8)) { return new SIMD.int16x8(s0, s1, s2, s3, s4, s5, s6, s7); } @@ -688,6 +702,20 @@ if (typeof SIMD.int16x8 === "undefined") { }); } +if (typeof SIMD.int16x8.check === "undefined") { + /** + * Check whether the argument is a int16x8. + * @param {int16x8} v An instance of int16x8. + * @return {int16x8} The int16x8 instance. + */ + SIMD.int16x8.check = function(v) { + if (!(v instanceof SIMD.int16x8)) { + throw new TypeError("argument is not a int16x8."); + } + return v; + } +} + if (typeof SIMD.int16x8.bool === "undefined") { /** * Construct a new instance of int16x8 number with true or false in each @@ -793,13 +821,6 @@ if (typeof SIMD.int8x16 === "undefined") { */ SIMD.int8x16 = function(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) { - if (arguments.length == 1) { - if (!(s0 instanceof SIMD.int8x16)) { - throw new TypeError("argument is not a int8x16."); - } - return s0; - } - if (!(this instanceof SIMD.int8x16)) { return new SIMD.int8x16(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); @@ -916,6 +937,20 @@ if (typeof SIMD.int8x16 === "undefined") { }); } +if (typeof SIMD.int8x16.check === "undefined") { + /** + * Check whether the argument is a int8x16. + * @param {int8x16} v An instance of int8x16. + * @return {int8x16} The int8x16 instance. + */ + SIMD.int8x16.check = function(v) { + if (!(v instanceof SIMD.int8x16)) { + throw new TypeError("argument is not a int8x16."); + } + return v; + } +} + if (typeof SIMD.int8x16.bool === "undefined") { /** * Construct a new instance of int8x16 number with true or false in each @@ -1024,7 +1059,7 @@ if (typeof SIMD.float32x4.abs === "undefined") { * t. */ SIMD.float32x4.abs = function(t) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(Math.abs(t.x), Math.abs(t.y), Math.abs(t.z), Math.abs(t.w)); } @@ -1037,7 +1072,7 @@ if (typeof SIMD.float32x4.neg === "undefined") { * t. */ SIMD.float32x4.neg = function(t) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(-t.x, -t.y, -t.z, -t.w); } } @@ -1049,8 +1084,8 @@ if (typeof SIMD.float32x4.add === "undefined") { * @return {float32x4} New instance of float32x4 with a + b. */ SIMD.float32x4.add = function(a, b) { - a = SIMD.float32x4(a); - b = SIMD.float32x4(b); + a = SIMD.float32x4.check(a); + b = SIMD.float32x4.check(b); return SIMD.float32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } } @@ -1062,8 +1097,8 @@ if (typeof SIMD.float32x4.sub === "undefined") { * @return {float32x4} New instance of float32x4 with a - b. */ SIMD.float32x4.sub = function(a, b) { - a = SIMD.float32x4(a); - b = SIMD.float32x4(b); + a = SIMD.float32x4.check(a); + b = SIMD.float32x4.check(b); return SIMD.float32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); } } @@ -1075,8 +1110,8 @@ if (typeof SIMD.float32x4.mul === "undefined") { * @return {float32x4} New instance of float32x4 with a * b. */ SIMD.float32x4.mul = function(a, b) { - a = SIMD.float32x4(a); - b = SIMD.float32x4(b); + a = SIMD.float32x4.check(a); + b = SIMD.float32x4.check(b); return SIMD.float32x4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); } } @@ -1088,8 +1123,8 @@ if (typeof SIMD.float32x4.div === "undefined") { * @return {float32x4} New instance of float32x4 with a / b. */ SIMD.float32x4.div = function(a, b) { - a = SIMD.float32x4(a); - b = SIMD.float32x4(b); + a = SIMD.float32x4.check(a); + b = SIMD.float32x4.check(b); return SIMD.float32x4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); } } @@ -1103,9 +1138,9 @@ if (typeof SIMD.float32x4.clamp === "undefined") { * between lowerLimit and upperLimit. */ SIMD.float32x4.clamp = function(t, lowerLimit, upperLimit) { - t = SIMD.float32x4(t); - lowerLimit = SIMD.float32x4(lowerLimit); - upperLimit = SIMD.float32x4(upperLimit); + t = SIMD.float32x4.check(t); + lowerLimit = SIMD.float32x4.check(lowerLimit); + upperLimit = SIMD.float32x4.check(upperLimit); var cx = t.x < lowerLimit.x ? lowerLimit.x : t.x; var cy = t.y < lowerLimit.y ? lowerLimit.y : t.y; var cz = t.z < lowerLimit.z ? lowerLimit.z : t.z; @@ -1126,8 +1161,8 @@ if (typeof SIMD.float32x4.min === "undefined") { * t and other. */ SIMD.float32x4.min = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = Math.min(t.x, other.x); var cy = Math.min(t.y, other.y); var cz = Math.min(t.z, other.z); @@ -1144,8 +1179,8 @@ if (typeof SIMD.float32x4.max === "undefined") { * t and other. */ SIMD.float32x4.max = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = Math.max(t.x, other.x); var cy = Math.max(t.y, other.y); var cz = Math.max(t.z, other.z); @@ -1162,8 +1197,8 @@ if (typeof SIMD.float32x4.minNum === "undefined") { * t and other, preferring numbers over NaNs. */ SIMD.float32x4.minNum = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = _SIMD_PRIVATE.minNum(t.x, other.x); var cy = _SIMD_PRIVATE.minNum(t.y, other.y); var cz = _SIMD_PRIVATE.minNum(t.z, other.z); @@ -1180,8 +1215,8 @@ if (typeof SIMD.float32x4.maxNum === "undefined") { * t and other, preferring numbers over NaNs. */ SIMD.float32x4.maxNum = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = _SIMD_PRIVATE.maxNum(t.x, other.x); var cy = _SIMD_PRIVATE.maxNum(t.y, other.y); var cz = _SIMD_PRIVATE.maxNum(t.z, other.z); @@ -1197,7 +1232,7 @@ if (typeof SIMD.float32x4.reciprocal === "undefined") { * t. */ SIMD.float32x4.reciprocal = function(t) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(1.0 / t.x, 1.0 / t.y, 1.0 / t.z, 1.0 / t.w); } } @@ -1209,7 +1244,7 @@ if (typeof SIMD.float32x4.reciprocalSqrt === "undefined") { * reciprocal value of t. */ SIMD.float32x4.reciprocalSqrt = function(t) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(Math.sqrt(1.0 / t.x), Math.sqrt(1.0 / t.y), Math.sqrt(1.0 / t.z), Math.sqrt(1.0 / t.w)); } @@ -1222,7 +1257,7 @@ if (typeof SIMD.float32x4.sqrt === "undefined") { * values of t. */ SIMD.float32x4.sqrt = function(t) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(Math.sqrt(t.x), Math.sqrt(t.y), Math.sqrt(t.z), Math.sqrt(t.w)); } @@ -1238,7 +1273,7 @@ if (typeof SIMD.float32x4.swizzle === "undefined") { * @return {float32x4} New instance of float32x4 with lanes swizzled. */ SIMD.float32x4.swizzle = function(t, x, y, z, w) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); _SIMD_PRIVATE._f32x4[0] = t.x; _SIMD_PRIVATE._f32x4[1] = t.y; _SIMD_PRIVATE._f32x4[2] = t.z; @@ -1262,8 +1297,8 @@ if (typeof SIMD.float32x4.shuffle === "undefined") { * @return {float32x4} New instance of float32x4 with lanes shuffled. */ SIMD.float32x4.shuffle = function(t1, t2, x, y, z, w) { - t1 = SIMD.float32x4(t1); - t2 = SIMD.float32x4(t2); + t1 = SIMD.float32x4.check(t1); + t2 = SIMD.float32x4.check(t2); var storage = _SIMD_PRIVATE._f32x8; storage[0] = t1.x; storage[1] = t1.y; @@ -1285,7 +1320,7 @@ if (typeof SIMD.float32x4.withX === "undefined") { * x replaced with {x}. */ SIMD.float32x4.withX = function(t, x) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(x, t.y, t.z, t.w); } } @@ -1298,7 +1333,7 @@ if (typeof SIMD.float32x4.withY === "undefined") { * y replaced with {y}. */ SIMD.float32x4.withY = function(t, y) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(t.x, y, t.z, t.w); } } @@ -1311,7 +1346,7 @@ if (typeof SIMD.float32x4.withZ === "undefined") { * z replaced with {z}. */ SIMD.float32x4.withZ = function(t, z) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(t.x, t.y, z, t.w); } } @@ -1324,7 +1359,7 @@ if (typeof SIMD.float32x4.withW === "undefined") { * w replaced with {w}. */ SIMD.float32x4.withW = function(t, w) { - t = SIMD.float32x4(t); + t = SIMD.float32x4.check(t); return SIMD.float32x4(t.x, t.y, t.z, w); } } @@ -1337,8 +1372,8 @@ if (typeof SIMD.float32x4.lessThan === "undefined") { * the result of t < other. */ SIMD.float32x4.lessThan = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = t.x < other.x; var cy = t.y < other.y; var cz = t.z < other.z; @@ -1355,8 +1390,8 @@ if (typeof SIMD.float32x4.lessThanOrEqual === "undefined") { * the result of t <= other. */ SIMD.float32x4.lessThanOrEqual = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = t.x <= other.x; var cy = t.y <= other.y; var cz = t.z <= other.z; @@ -1373,8 +1408,8 @@ if (typeof SIMD.float32x4.equal === "undefined") { * the result of t == other. */ SIMD.float32x4.equal = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = t.x == other.x; var cy = t.y == other.y; var cz = t.z == other.z; @@ -1391,8 +1426,8 @@ if (typeof SIMD.float32x4.notEqual === "undefined") { * the result of t != other. */ SIMD.float32x4.notEqual = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = t.x != other.x; var cy = t.y != other.y; var cz = t.z != other.z; @@ -1409,8 +1444,8 @@ if (typeof SIMD.float32x4.greaterThanOrEqual === "undefined") { * the result of t >= other. */ SIMD.float32x4.greaterThanOrEqual = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = t.x >= other.x; var cy = t.y >= other.y; var cz = t.z >= other.z; @@ -1427,8 +1462,8 @@ if (typeof SIMD.float32x4.greaterThan === "undefined") { * the result of t > other. */ SIMD.float32x4.greaterThan = function(t, other) { - t = SIMD.float32x4(t); - other = SIMD.float32x4(other); + t = SIMD.float32x4.check(t); + other = SIMD.float32x4.check(other); var cx = t.x > other.x; var cy = t.y > other.y; var cz = t.z > other.z; @@ -1448,9 +1483,9 @@ if (typeof SIMD.float32x4.select === "undefined") { * indicated */ SIMD.float32x4.select = function(t, trueValue, falseValue) { - t = SIMD.int32x4(t); - trueValue = SIMD.float32x4(trueValue); - falseValue = SIMD.float32x4(falseValue); + t = SIMD.int32x4.check(t); + trueValue = SIMD.float32x4.check(trueValue); + falseValue = SIMD.float32x4.check(falseValue); return SIMD.float32x4(_SIMD_PRIVATE.tobool(t.x) ? trueValue.x : falseValue.x, _SIMD_PRIVATE.tobool(t.y) ? trueValue.y : falseValue.y, _SIMD_PRIVATE.tobool(t.z) ? trueValue.z : falseValue.z, @@ -1469,9 +1504,9 @@ if (typeof SIMD.float32x4.bitselect === "undefined") { * indicated */ SIMD.float32x4.bitselect = function(t, trueValue, falseValue) { - t = SIMD.int32x4(t); - trueValue = SIMD.float32x4(trueValue); - falseValue = SIMD.float32x4(falseValue); + t = SIMD.int32x4.check(t); + trueValue = SIMD.float32x4.check(trueValue); + falseValue = SIMD.float32x4.check(falseValue); var tv = SIMD.int32x4.fromFloat32x4Bits(trueValue); var fv = SIMD.int32x4.fromFloat32x4Bits(falseValue); var tr = SIMD.int32x4.and(t, tv); @@ -1487,8 +1522,8 @@ if (typeof SIMD.float32x4.and === "undefined") { * @return {float32x4} New instance of float32x4 with values of a & b. */ SIMD.float32x4.and = function(a, b) { - a = SIMD.float32x4(a); - b = SIMD.float32x4(b); + a = SIMD.float32x4.check(a); + b = SIMD.float32x4.check(b); var aInt = SIMD.int32x4.fromFloat32x4Bits(a); var bInt = SIMD.int32x4.fromFloat32x4Bits(b); return SIMD.float32x4.fromInt32x4Bits(SIMD.int32x4.and(aInt, bInt)); @@ -1502,8 +1537,8 @@ if (typeof SIMD.float32x4.or === "undefined") { * @return {float32x4} New instance of float32x4 with values of a | b. */ SIMD.float32x4.or = function(a, b) { - a = SIMD.float32x4(a); - b = SIMD.float32x4(b); + a = SIMD.float32x4.check(a); + b = SIMD.float32x4.check(b); var aInt = SIMD.int32x4.fromFloat32x4Bits(a); var bInt = SIMD.int32x4.fromFloat32x4Bits(b); return SIMD.float32x4.fromInt32x4Bits(SIMD.int32x4.or(aInt, bInt)); @@ -1517,8 +1552,8 @@ if (typeof SIMD.float32x4.xor === "undefined") { * @return {float32x4} New instance of float32x4 with values of a ^ b. */ SIMD.float32x4.xor = function(a, b) { - a = SIMD.float32x4(a); - b = SIMD.float32x4(b); + a = SIMD.float32x4.check(a); + b = SIMD.float32x4.check(b); var aInt = SIMD.int32x4.fromFloat32x4Bits(a); var bInt = SIMD.int32x4.fromFloat32x4Bits(b); return SIMD.float32x4.fromInt32x4Bits(SIMD.int32x4.xor(aInt, bInt)); @@ -1531,7 +1566,7 @@ if (typeof SIMD.float32x4.not === "undefined") { * @return {float32x4} New instance of float32x4 with values of ~a. */ SIMD.float32x4.not = function(a) { - a = SIMD.float32x4(a); + a = SIMD.float32x4.check(a); var aInt = SIMD.int32x4.fromFloat32x4Bits(a); return SIMD.float32x4.fromInt32x4Bits(SIMD.int32x4.not(aInt)); } @@ -1656,7 +1691,7 @@ if (typeof SIMD.float32x4.store === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 16) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.float32x4(value); + value = SIMD.float32x4.check(value); _SIMD_PRIVATE._f32x4[0] = value.x; _SIMD_PRIVATE._f32x4[1] = value.y; _SIMD_PRIVATE._f32x4[2] = value.z; @@ -1686,7 +1721,7 @@ if (typeof SIMD.float32x4.storeX === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 4) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.float32x4(value); + value = SIMD.float32x4.check(value); if (bpe == 8) { // tarray's elements are too wide. Just create a new view; this is rare. var view = new Float32Array(tarray.buffer, tarray.byteOffset + index * 8, 1); @@ -1718,7 +1753,7 @@ if (typeof SIMD.float32x4.storeXY === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 8) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.float32x4(value); + value = SIMD.float32x4.check(value); _SIMD_PRIVATE._f32x4[0] = value.x; _SIMD_PRIVATE._f32x4[1] = value.y; var array = bpe == 1 ? _SIMD_PRIVATE._i8x16 : @@ -1746,7 +1781,7 @@ if (typeof SIMD.float32x4.storeXYZ === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 12) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.float32x4(value); + value = SIMD.float32x4.check(value); if (bpe == 8) { // tarray's elements are too wide. Just create a new view; this is rare. var view = new Float32Array(tarray.buffer, tarray.byteOffset + index * 8, 3); @@ -1774,7 +1809,7 @@ if (typeof SIMD.float64x2.abs === "undefined") { * t. */ SIMD.float64x2.abs = function(t) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float64x2(Math.abs(t.x), Math.abs(t.y)); } } @@ -1786,7 +1821,7 @@ if (typeof SIMD.float64x2.neg === "undefined") { * t. */ SIMD.float64x2.neg = function(t) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float64x2(-t.x, -t.y); } } @@ -1798,8 +1833,8 @@ if (typeof SIMD.float64x2.add === "undefined") { * @return {float64x2} New instance of float64x2 with a + b. */ SIMD.float64x2.add = function(a, b) { - a = SIMD.float64x2(a); - b = SIMD.float64x2(b); + a = SIMD.float64x2.check(a); + b = SIMD.float64x2.check(b); return SIMD.float64x2(a.x + b.x, a.y + b.y); } } @@ -1811,8 +1846,8 @@ if (typeof SIMD.float64x2.sub === "undefined") { * @return {float64x2} New instance of float64x2 with a - b. */ SIMD.float64x2.sub = function(a, b) { - a = SIMD.float64x2(a); - b = SIMD.float64x2(b); + a = SIMD.float64x2.check(a); + b = SIMD.float64x2.check(b); return SIMD.float64x2(a.x - b.x, a.y - b.y); } } @@ -1824,8 +1859,8 @@ if (typeof SIMD.float64x2.mul === "undefined") { * @return {float64x2} New instance of float64x2 with a * b. */ SIMD.float64x2.mul = function(a, b) { - a = SIMD.float64x2(a); - b = SIMD.float64x2(b); + a = SIMD.float64x2.check(a); + b = SIMD.float64x2.check(b); return SIMD.float64x2(a.x * b.x, a.y * b.y); } } @@ -1837,8 +1872,8 @@ if (typeof SIMD.float64x2.div === "undefined") { * @return {float64x2} New instance of float64x2 with a / b. */ SIMD.float64x2.div = function(a, b) { - a = SIMD.float64x2(a); - b = SIMD.float64x2(b); + a = SIMD.float64x2.check(a); + b = SIMD.float64x2.check(b); return SIMD.float64x2(a.x / b.x, a.y / b.y); } } @@ -1852,9 +1887,9 @@ if (typeof SIMD.float64x2.clamp === "undefined") { * between lowerLimit and upperLimit. */ SIMD.float64x2.clamp = function(t, lowerLimit, upperLimit) { - t = SIMD.float64x2(t); - lowerLimit = SIMD.float64x2(lowerLimit); - upperLimit = SIMD.float64x2(upperLimit); + t = SIMD.float64x2.check(t); + lowerLimit = SIMD.float64x2.check(lowerLimit); + upperLimit = SIMD.float64x2.check(upperLimit); var cx = t.x < lowerLimit.x ? lowerLimit.x : t.x; var cy = t.y < lowerLimit.y ? lowerLimit.y : t.y; cx = cx > upperLimit.x ? upperLimit.x : cx; @@ -1871,8 +1906,8 @@ if (typeof SIMD.float64x2.min === "undefined") { * t and other. */ SIMD.float64x2.min = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = Math.min(t.x, other.x); var cy = Math.min(t.y, other.y); return SIMD.float64x2(cx, cy); @@ -1887,8 +1922,8 @@ if (typeof SIMD.float64x2.max === "undefined") { * t and other. */ SIMD.float64x2.max = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = Math.max(t.x, other.x); var cy = Math.max(t.y, other.y); return SIMD.float64x2(cx, cy); @@ -1903,8 +1938,8 @@ if (typeof SIMD.float64x2.minNum === "undefined") { * t and other, preferring numbers over NaNs. */ SIMD.float64x2.minNum = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = _SIMD_PRIVATE.minNum(t.x, other.x); var cy = _SIMD_PRIVATE.minNum(t.y, other.y); return SIMD.float64x2(cx, cy); @@ -1919,8 +1954,8 @@ if (typeof SIMD.float64x2.maxNum === "undefined") { * t and other, preferring numbers over NaNs. */ SIMD.float64x2.maxNum = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = _SIMD_PRIVATE.maxNum(t.x, other.x); var cy = _SIMD_PRIVATE.maxNum(t.y, other.y); return SIMD.float64x2(cx, cy); @@ -1934,7 +1969,7 @@ if (typeof SIMD.float64x2.reciprocal === "undefined") { * t. */ SIMD.float64x2.reciprocal = function(t) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float64x2(1.0 / t.x, 1.0 / t.y); } } @@ -1946,7 +1981,7 @@ if (typeof SIMD.float64x2.reciprocalSqrt === "undefined") { * reciprocal value of t. */ SIMD.float64x2.reciprocalSqrt = function(t) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float64x2(Math.sqrt(1.0 / t.x), Math.sqrt(1.0 / t.y)); } } @@ -1958,7 +1993,7 @@ if (typeof SIMD.float64x2.sqrt === "undefined") { * values of t. */ SIMD.float64x2.sqrt = function(t) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float64x2(Math.sqrt(t.x), Math.sqrt(t.y)); } } @@ -1971,7 +2006,7 @@ if (typeof SIMD.float64x2.swizzle === "undefined") { * @return {float64x2} New instance of float64x2 with lanes swizzled. */ SIMD.float64x2.swizzle = function(t, x, y) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); var storage = _SIMD_PRIVATE._f64x2; storage[0] = t.x; storage[1] = t.y; @@ -1991,8 +2026,8 @@ if (typeof SIMD.float64x2.shuffle === "undefined") { * @return {float64x2} New instance of float64x2 with lanes shuffled. */ SIMD.float64x2.shuffle = function(t1, t2, x, y) { - t1 = SIMD.float64x2(t1); - t2 = SIMD.float64x2(t2); + t1 = SIMD.float64x2.check(t1); + t2 = SIMD.float64x2.check(t2); var storage = _SIMD_PRIVATE._f64x4; storage[0] = t1.x; storage[1] = t1.y; @@ -2010,7 +2045,7 @@ if (typeof SIMD.float64x2.withX === "undefined") { * x replaced with {x}. */ SIMD.float64x2.withX = function(t, x) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float64x2(x, t.y); } } @@ -2023,7 +2058,7 @@ if (typeof SIMD.float64x2.withY === "undefined") { * y replaced with {y}. */ SIMD.float64x2.withY = function(t, y) { - t = SIMD.float64x2(t); + t = SIMD.float64x2.check(t); return SIMD.float64x2(t.x, y); } } @@ -2036,8 +2071,8 @@ if (typeof SIMD.float64x2.lessThan === "undefined") { * the result of t < other. */ SIMD.float64x2.lessThan = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = t.x < other.x; var cy = t.y < other.y; return SIMD.int32x4.bool(cx, cx, cy, cy); @@ -2052,8 +2087,8 @@ if (typeof SIMD.float64x2.lessThanOrEqual === "undefined") { * the result of t <= other. */ SIMD.float64x2.lessThanOrEqual = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = t.x <= other.x; var cy = t.y <= other.y; return SIMD.int32x4.bool(cx, cx, cy, cy); @@ -2068,8 +2103,8 @@ if (typeof SIMD.float64x2.equal === "undefined") { * the result of t == other. */ SIMD.float64x2.equal = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = t.x == other.x; var cy = t.y == other.y; return SIMD.int32x4.bool(cx, cx, cy, cy); @@ -2084,8 +2119,8 @@ if (typeof SIMD.float64x2.notEqual === "undefined") { * the result of t != other. */ SIMD.float64x2.notEqual = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = t.x != other.x; var cy = t.y != other.y; return SIMD.int32x4.bool(cx, cx, cy, cy); @@ -2100,8 +2135,8 @@ if (typeof SIMD.float64x2.greaterThanOrEqual === "undefined") { * the result of t >= other. */ SIMD.float64x2.greaterThanOrEqual = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = t.x >= other.x; var cy = t.y >= other.y; return SIMD.int32x4.bool(cx, cx, cy, cy); @@ -2116,8 +2151,8 @@ if (typeof SIMD.float64x2.greaterThan === "undefined") { * the result of t > other. */ SIMD.float64x2.greaterThan = function(t, other) { - t = SIMD.float64x2(t); - other = SIMD.float64x2(other); + t = SIMD.float64x2.check(t); + other = SIMD.float64x2.check(other); var cx = t.x > other.x; var cy = t.y > other.y; return SIMD.int32x4.bool(cx, cx, cy, cy); @@ -2135,9 +2170,9 @@ if (typeof SIMD.float64x2.select === "undefined") { * indicated */ SIMD.float64x2.select = function(t, trueValue, falseValue) { - t = SIMD.int32x4(t); - trueValue = SIMD.float64x2(trueValue); - falseValue = SIMD.float64x2(falseValue); + t = SIMD.int32x4.check(t); + trueValue = SIMD.float64x2.check(trueValue); + falseValue = SIMD.float64x2.check(falseValue); return SIMD.float64x2(_SIMD_PRIVATE.tobool(t.x) ? trueValue.x : falseValue.x, _SIMD_PRIVATE.tobool(t.y) ? trueValue.y : falseValue.y); } @@ -2154,9 +2189,9 @@ if (typeof SIMD.float64x2.bitselect === "undefined") { * indicated */ SIMD.float64x2.bitselect = function(t, trueValue, falseValue) { - t = SIMD.int32x4(t); - trueValue = SIMD.float64x2(trueValue); - falseValue = SIMD.float64x2(falseValue); + t = SIMD.int32x4.check(t); + trueValue = SIMD.float64x2.check(trueValue); + falseValue = SIMD.float64x2.check(falseValue); var tv = SIMD.int32x4.fromFloat64x2Bits(trueValue); var fv = SIMD.int32x4.fromFloat64x2Bits(falseValue); var tr = SIMD.int32x4.and(t, tv); @@ -2232,7 +2267,7 @@ if (typeof SIMD.float64x2.store === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 16) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.float64x2(value); + value = SIMD.float64x2.check(value); _SIMD_PRIVATE._f64x2[0] = value.x; _SIMD_PRIVATE._f64x2[1] = value.y; var array = bpe == 1 ? _SIMD_PRIVATE._i8x16 : @@ -2260,7 +2295,7 @@ if (typeof SIMD.float64x2.storeX === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 8) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.float64x2(value); + value = SIMD.float64x2.check(value); _SIMD_PRIVATE._f64x2[0] = value.x; var array = bpe == 1 ? _SIMD_PRIVATE._i8x16 : bpe == 2 ? _SIMD_PRIVATE._i16x8 : @@ -2279,8 +2314,8 @@ if (typeof SIMD.int32x4.and === "undefined") { * @return {int32x4} New instance of int32x4 with values of a & b. */ SIMD.int32x4.and = function(a, b) { - a = SIMD.int32x4(a); - b = SIMD.int32x4(b); + a = SIMD.int32x4.check(a); + b = SIMD.int32x4.check(b); return SIMD.int32x4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); } } @@ -2292,8 +2327,8 @@ if (typeof SIMD.int32x4.or === "undefined") { * @return {int32x4} New instance of int32x4 with values of a | b. */ SIMD.int32x4.or = function(a, b) { - a = SIMD.int32x4(a); - b = SIMD.int32x4(b); + a = SIMD.int32x4.check(a); + b = SIMD.int32x4.check(b); return SIMD.int32x4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); } } @@ -2305,8 +2340,8 @@ if (typeof SIMD.int32x4.xor === "undefined") { * @return {int32x4} New instance of int32x4 with values of a ^ b. */ SIMD.int32x4.xor = function(a, b) { - a = SIMD.int32x4(a); - b = SIMD.int32x4(b); + a = SIMD.int32x4.check(a); + b = SIMD.int32x4.check(b); return SIMD.int32x4(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); } } @@ -2317,7 +2352,7 @@ if (typeof SIMD.int32x4.not === "undefined") { * @return {int32x4} New instance of int32x4 with values of ~t */ SIMD.int32x4.not = function(t) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.int32x4(~t.x, ~t.y, ~t.z, ~t.w); } } @@ -2328,7 +2363,7 @@ if (typeof SIMD.int32x4.neg === "undefined") { * @return {int32x4} New instance of int32x4 with values of -t */ SIMD.int32x4.neg = function(t) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.int32x4(-t.x, -t.y, -t.z, -t.w); } } @@ -2340,8 +2375,8 @@ if (typeof SIMD.int32x4.add === "undefined") { * @return {int32x4} New instance of int32x4 with values of a + b. */ SIMD.int32x4.add = function(a, b) { - a = SIMD.int32x4(a); - b = SIMD.int32x4(b); + a = SIMD.int32x4.check(a); + b = SIMD.int32x4.check(b); return SIMD.int32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } } @@ -2353,8 +2388,8 @@ if (typeof SIMD.int32x4.sub === "undefined") { * @return {int32x4} New instance of int32x4 with values of a - b. */ SIMD.int32x4.sub = function(a, b) { - a = SIMD.int32x4(a); - b = SIMD.int32x4(b); + a = SIMD.int32x4.check(a); + b = SIMD.int32x4.check(b); return SIMD.int32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); } } @@ -2366,8 +2401,8 @@ if (typeof SIMD.int32x4.mul === "undefined") { * @return {int32x4} New instance of int32x4 with values of a * b. */ SIMD.int32x4.mul = function(a, b) { - a = SIMD.int32x4(a); - b = SIMD.int32x4(b); + a = SIMD.int32x4.check(a); + b = SIMD.int32x4.check(b); return SIMD.int32x4(Math.imul(a.x, b.x), Math.imul(a.y, b.y), Math.imul(a.z, b.z), Math.imul(a.w, b.w)); } @@ -2383,7 +2418,7 @@ if (typeof SIMD.int32x4.swizzle === "undefined") { * @return {int32x4} New instance of int32x4 with lanes swizzled. */ SIMD.int32x4.swizzle = function(t, x, y, z, w) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); var storage = _SIMD_PRIVATE._i32x4; storage[0] = t.x; storage[1] = t.y; @@ -2407,8 +2442,8 @@ if (typeof SIMD.int32x4.shuffle === "undefined") { * @return {int32x4} New instance of int32x4 with lanes shuffled. */ SIMD.int32x4.shuffle = function(t1, t2, x, y, z, w) { - t1 = SIMD.int32x4(t1); - t2 = SIMD.int32x4(t2); + t1 = SIMD.int32x4.check(t1); + t2 = SIMD.int32x4.check(t2); var storage = _SIMD_PRIVATE._i32x8; storage[0] = t1.x; storage[1] = t1.y; @@ -2433,9 +2468,9 @@ if (typeof SIMD.int32x4.select === "undefined") { * indicated */ SIMD.int32x4.select = function(t, trueValue, falseValue) { - t = SIMD.int32x4(t); - trueValue = SIMD.int32x4(trueValue); - falseValue = SIMD.int32x4(falseValue); + t = SIMD.int32x4.check(t); + trueValue = SIMD.int32x4.check(trueValue); + falseValue = SIMD.int32x4.check(falseValue); return SIMD.int32x4(_SIMD_PRIVATE.tobool(t.x) ? trueValue.x : falseValue.x, _SIMD_PRIVATE.tobool(t.y) ? trueValue.y : falseValue.y, _SIMD_PRIVATE.tobool(t.z) ? trueValue.z : falseValue.z, @@ -2454,9 +2489,9 @@ if (typeof SIMD.int32x4.bitselect === "undefined") { * indicated */ SIMD.int32x4.bitselect = function(t, trueValue, falseValue) { - t = SIMD.int32x4(t); - trueValue = SIMD.int32x4(trueValue); - falseValue = SIMD.int32x4(falseValue); + t = SIMD.int32x4.check(t); + trueValue = SIMD.int32x4.check(trueValue); + falseValue = SIMD.int32x4.check(falseValue); var tr = SIMD.int32x4.and(t, trueValue); var fr = SIMD.int32x4.and(SIMD.int32x4.not(t), falseValue); return SIMD.int32x4.or(tr, fr); @@ -2471,7 +2506,7 @@ if (typeof SIMD.int32x4.withX === "undefined") { * x lane replaced with {x}. */ SIMD.int32x4.withX = function(t, x) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.int32x4(x, t.y, t.z, t.w); } } @@ -2484,7 +2519,7 @@ if (typeof SIMD.int32x4.withY === "undefined") { * y lane replaced with {y}. */ SIMD.int32x4.withY = function(t, y) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.int32x4(t.x, y, t.z, t.w); } } @@ -2497,7 +2532,7 @@ if (typeof SIMD.int32x4.withZ === "undefined") { * z lane replaced with {z}. */ SIMD.int32x4.withZ = function(t, z) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.int32x4(t.x, t.y, z, t.w); } } @@ -2510,7 +2545,7 @@ if (typeof SIMD.int32x4.withW === "undefined") { * w lane replaced with {w}. */ SIMD.int32x4.withW = function(t, w) { - t = SIMD.int32x4(t); + t = SIMD.int32x4.check(t); return SIMD.int32x4(t.x, t.y, t.z, w); } } @@ -2523,8 +2558,8 @@ if (typeof SIMD.int32x4.equal === "undefined") { * the result of t == other. */ SIMD.int32x4.equal = function(t, other) { - t = SIMD.int32x4(t); - other = SIMD.int32x4(other); + t = SIMD.int32x4.check(t); + other = SIMD.int32x4.check(other); var cx = t.x == other.x; var cy = t.y == other.y; var cz = t.z == other.z; @@ -2541,8 +2576,8 @@ if (typeof SIMD.int32x4.notEqual === "undefined") { * the result of t != other. */ SIMD.int32x4.notEqual = function(t, other) { - t = SIMD.int32x4(t); - other = SIMD.int32x4(other); + t = SIMD.int32x4.check(t); + other = SIMD.int32x4.check(other); var cx = t.x != other.x; var cy = t.y != other.y; var cz = t.z != other.z; @@ -2559,8 +2594,8 @@ if (typeof SIMD.int32x4.greaterThan === "undefined") { * the result of t > other. */ SIMD.int32x4.greaterThan = function(t, other) { - t = SIMD.int32x4(t); - other = SIMD.int32x4(other); + t = SIMD.int32x4.check(t); + other = SIMD.int32x4.check(other); var cx = t.x > other.x; var cy = t.y > other.y; var cz = t.z > other.z; @@ -2577,8 +2612,8 @@ if (typeof SIMD.int32x4.greaterThanOrEqual === "undefined") { * the result of t >= other. */ SIMD.int32x4.greaterThanOrEqual = function(t, other) { - t = SIMD.int32x4(t); - other = SIMD.int32x4(other); + t = SIMD.int32x4.check(t); + other = SIMD.int32x4.check(other); var cx = t.x >= other.x; var cy = t.y >= other.y; var cz = t.z >= other.z; @@ -2595,8 +2630,8 @@ if (typeof SIMD.int32x4.lessThan === "undefined") { * the result of t < other. */ SIMD.int32x4.lessThan = function(t, other) { - t = SIMD.int32x4(t); - other = SIMD.int32x4(other); + t = SIMD.int32x4.check(t); + other = SIMD.int32x4.check(other); var cx = t.x < other.x; var cy = t.y < other.y; var cz = t.z < other.z; @@ -2613,8 +2648,8 @@ if (typeof SIMD.int32x4.lessThanOrEqual === "undefined") { * the result of t <= other. */ SIMD.int32x4.lessThanOrEqual = function(t, other) { - t = SIMD.int32x4(t); - other = SIMD.int32x4(other); + t = SIMD.int32x4.check(t); + other = SIMD.int32x4.check(other); var cx = t.x <= other.x; var cy = t.y <= other.y; var cz = t.z <= other.z; @@ -2630,9 +2665,7 @@ if (typeof SIMD.int32x4.shiftLeftByScalar === "undefined") { * @return {int32x4} lanes in a shifted by bits. */ SIMD.int32x4.shiftLeftByScalar = function(a, bits) { - a = SIMD.int32x4(a); - if (bits>>>0 >= 32) - return SIMD.int32x4.splat(0.0); + a = SIMD.int32x4.check(a); var x = a.x << bits; var y = a.y << bits; var z = a.z << bits; @@ -2648,9 +2681,7 @@ if (typeof SIMD.int32x4.shiftRightLogicalByScalar === "undefined") { * @return {int32x4} lanes in a shifted by bits. */ SIMD.int32x4.shiftRightLogicalByScalar = function(a, bits) { - a = SIMD.int32x4(a); - if (bits>>>0 >= 32) - return SIMD.int32x4.splat(0.0); + a = SIMD.int32x4.check(a); var x = a.x >>> bits; var y = a.y >>> bits; var z = a.z >>> bits; @@ -2666,9 +2697,7 @@ if (typeof SIMD.int32x4.shiftRightArithmeticByScalar === "undefined") { * @return {int32x4} lanes in a shifted by bits. */ SIMD.int32x4.shiftRightArithmeticByScalar = function(a, bits) { - a = SIMD.int32x4(a); - if (bits>>>0 >= 32) - bits = 31; + a = SIMD.int32x4.check(a); var x = a.x >> bits; var y = a.y >> bits; var z = a.z >> bits; @@ -2796,7 +2825,7 @@ if (typeof SIMD.int32x4.store === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 16) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.int32x4(value); + value = SIMD.int32x4.check(value); _SIMD_PRIVATE._i32x4[0] = value.x; _SIMD_PRIVATE._i32x4[1] = value.y; _SIMD_PRIVATE._i32x4[2] = value.z; @@ -2826,7 +2855,7 @@ if (typeof SIMD.int32x4.storeX === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 4) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.int32x4(value); + value = SIMD.int32x4.check(value); if (bpe == 8) { // tarray's elements are too wide. Just create a new view; this is rare. var view = new Int32Array(tarray.buffer, tarray.byteOffset + index * 8, 1); @@ -2858,7 +2887,7 @@ if (typeof SIMD.int32x4.storeXY === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 8) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.int32x4(value); + value = SIMD.int32x4.check(value); _SIMD_PRIVATE._i32x4[0] = value.x; _SIMD_PRIVATE._i32x4[1] = value.y; var array = bpe == 1 ? _SIMD_PRIVATE._i8x16 : @@ -2886,7 +2915,7 @@ if (typeof SIMD.int32x4.storeXYZ === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 12) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.int32x4(value); + value = SIMD.int32x4.check(value); if (bpe == 8) { // tarray's elements are too wide. Just create a new view; this is rare. var view = new Int32Array(tarray.buffer, tarray.byteOffset + index * 8, 3); @@ -2914,8 +2943,8 @@ if (typeof SIMD.int16x8.and === "undefined") { * @return {int16x8} New instance of int16x8 with values of a & b. */ SIMD.int16x8.and = function(a, b) { - a = SIMD.int16x8(a); - b = SIMD.int16x8(b); + a = SIMD.int16x8.check(a); + b = SIMD.int16x8.check(b); return SIMD.int16x8(a.s0 & b.s0, a.s1 & b.s1, a.s2 & b.s2, a.s3 & b.s3, a.s4 & b.s4, a.s5 & b.s5, a.s6 & b.s6, a.s7 & b.s7); } @@ -2928,8 +2957,8 @@ if (typeof SIMD.int16x8.or === "undefined") { * @return {int16x8} New instance of int16x8 with values of a | b. */ SIMD.int16x8.or = function(a, b) { - a = SIMD.int16x8(a); - b = SIMD.int16x8(b); + a = SIMD.int16x8.check(a); + b = SIMD.int16x8.check(b); return SIMD.int16x8(a.s0 | b.s0, a.s1 | b.s1, a.s2 | b.s2, a.s3 | b.s3, a.s4 | b.s4, a.s5 | b.s5, a.s6 | b.s6, a.s7 | b.s7); } @@ -2942,8 +2971,8 @@ if (typeof SIMD.int16x8.xor === "undefined") { * @return {int16x8} New instance of int16x8 with values of a ^ b. */ SIMD.int16x8.xor = function(a, b) { - a = SIMD.int16x8(a); - b = SIMD.int16x8(b); + a = SIMD.int16x8.check(a); + b = SIMD.int16x8.check(b); return SIMD.int16x8(a.s0 ^ b.s0, a.s1 ^ b.s1, a.s2 ^ b.s2, a.s3 ^ b.s3, a.s4 ^ b.s4, a.s5 ^ b.s5, a.s6 ^ b.s6, a.s7 ^ b.s7); } @@ -2955,7 +2984,7 @@ if (typeof SIMD.int16x8.not === "undefined") { * @return {int16x8} New instance of int16x8 with values of ~t */ SIMD.int16x8.not = function(t) { - t = SIMD.int16x8(t); + t = SIMD.int16x8.check(t); return SIMD.int16x8(~t.s0, ~t.s1, ~t.s2, ~t.s3, ~t.s4, ~t.s5, ~t.s6, ~t.s7); } @@ -2967,7 +2996,7 @@ if (typeof SIMD.int16x8.neg === "undefined") { * @return {int16x8} New instance of int16x8 with values of -t */ SIMD.int16x8.neg = function(t) { - t = SIMD.int16x8(t); + t = SIMD.int16x8.check(t); return SIMD.int16x8(-t.s0, -t.s1, -t.s2, -t.s3, -t.s4, -t.s5, -t.s6, -t.s7); } @@ -2980,8 +3009,8 @@ if (typeof SIMD.int16x8.add === "undefined") { * @return {int16x8} New instance of int16x8 with values of a + b. */ SIMD.int16x8.add = function(a, b) { - a = SIMD.int16x8(a); - b = SIMD.int16x8(b); + a = SIMD.int16x8.check(a); + b = SIMD.int16x8.check(b); return SIMD.int16x8(a.s0 + b.s0, a.s1 + b.s1, a.s2 + b.s2, a.s3 + b.s3, a.s4 + b.s4, a.s5 + b.s5, a.s6 + b.s6, a.s7 + b.s7); } @@ -2994,8 +3023,8 @@ if (typeof SIMD.int16x8.sub === "undefined") { * @return {int16x8} New instance of int16x8 with values of a - b. */ SIMD.int16x8.sub = function(a, b) { - a = SIMD.int16x8(a); - b = SIMD.int16x8(b); + a = SIMD.int16x8.check(a); + b = SIMD.int16x8.check(b); return SIMD.int16x8(a.s0 - b.s0, a.s1 - b.s1, a.s2 - b.s2, a.s3 - b.s3, a.s4 - b.s4, a.s5 - b.s5, a.s6 - b.s6, a.s7 - b.s7); } @@ -3008,8 +3037,8 @@ if (typeof SIMD.int16x8.mul === "undefined") { * @return {int16x8} New instance of int16x8 with values of a * b. */ SIMD.int16x8.mul = function(a, b) { - a = SIMD.int16x8(a); - b = SIMD.int16x8(b); + a = SIMD.int16x8.check(a); + b = SIMD.int16x8.check(b); return SIMD.int16x8(Math.imul(a.s0, b.s0), Math.imul(a.s1, b.s1), Math.imul(a.s2, b.s2), Math.imul(a.s3, b.s3), Math.imul(a.s4, b.s4), Math.imul(a.s5, b.s5), @@ -3028,9 +3057,34 @@ if (typeof SIMD.int16x8.select === "undefined") { * indicated */ SIMD.int16x8.select = function(t, trueValue, falseValue) { - t = SIMD.int16x8(t); - trueValue = SIMD.int16x8(trueValue); - falseValue = SIMD.int16x8(falseValue); + t = SIMD.int16x8.check(t); + trueValue = SIMD.int16x8.check(trueValue); + falseValue = SIMD.int16x8.check(falseValue); + return SIMD.int16x8(_SIMD_PRIVATE.tobool(t.s0) ? trueValue.s0 : falseValue.s0, + _SIMD_PRIVATE.tobool(t.s1) ? trueValue.s1 : falseValue.s1, + _SIMD_PRIVATE.tobool(t.s2) ? trueValue.s2 : falseValue.s2, + _SIMD_PRIVATE.tobool(t.s3) ? trueValue.s3 : falseValue.s3, + _SIMD_PRIVATE.tobool(t.s4) ? trueValue.s4 : falseValue.s4, + _SIMD_PRIVATE.tobool(t.s5) ? trueValue.s5 : falseValue.s5, + _SIMD_PRIVATE.tobool(t.s6) ? trueValue.s6 : falseValue.s6, + _SIMD_PRIVATE.tobool(t.s7) ? trueValue.s7 : falseValue.s7); + } +} + +if (typeof SIMD.int16x8.bitselect === "undefined") { + /** + * @param {int16x8} t Selector mask. An instance of int16x8 + * @param {int16x8} trueValue Pick lane from here if corresponding + * selector bit is 1 + * @param {int16x8} falseValue Pick lane from here if corresponding + * selector bit is 0 + * @return {int16x8} Mix of lanes from trueValue or falseValue as + * indicated + */ + SIMD.int16x8.bitselect = function(t, trueValue, falseValue) { + t = SIMD.int16x8.check(t); + trueValue = SIMD.int16x8.check(trueValue); + falseValue = SIMD.int16x8.check(falseValue); var tr = SIMD.int16x8.and(t, trueValue); var fr = SIMD.int16x8.and(SIMD.int16x8.not(t), falseValue); return SIMD.int16x8.or(tr, fr); @@ -3045,8 +3099,8 @@ if (typeof SIMD.int16x8.equal === "undefined") { * the result of t == other. */ SIMD.int16x8.equal = function(t, other) { - t = SIMD.int16x8(t); - other = SIMD.int16x8(other); + t = SIMD.int16x8.check(t); + other = SIMD.int16x8.check(other); var cs0 = t.s0 == other.s0; var cs1 = t.s1 == other.s1; var cs2 = t.s2 == other.s2; @@ -3067,8 +3121,8 @@ if (typeof SIMD.int16x8.notEqual === "undefined") { * the result of t != other. */ SIMD.int16x8.notEqual = function(t, other) { - t = SIMD.int16x8(t); - other = SIMD.int16x8(other); + t = SIMD.int16x8.check(t); + other = SIMD.int16x8.check(other); var cs0 = t.s0 != other.s0; var cs1 = t.s1 != other.s1; var cs2 = t.s2 != other.s2; @@ -3089,8 +3143,8 @@ if (typeof SIMD.int16x8.greaterThan === "undefined") { * the result of t > other. */ SIMD.int16x8.greaterThan = function(t, other) { - t = SIMD.int16x8(t); - other = SIMD.int16x8(other); + t = SIMD.int16x8.check(t); + other = SIMD.int16x8.check(other); var cs0 = t.s0 > other.s0; var cs1 = t.s1 > other.s1; var cs2 = t.s2 > other.s2; @@ -3111,8 +3165,8 @@ if (typeof SIMD.int16x8.greaterThanOrEqual === "undefined") { * the result of t >= other. */ SIMD.int16x8.greaterThanOrEqual = function(t, other) { - t = SIMD.int16x8(t); - other = SIMD.int16x8(other); + t = SIMD.int16x8.check(t); + other = SIMD.int16x8.check(other); var cs0 = t.s0 >= other.s0; var cs1 = t.s1 >= other.s1; var cs2 = t.s2 >= other.s2; @@ -3133,8 +3187,8 @@ if (typeof SIMD.int16x8.lessThan === "undefined") { * the result of t < other. */ SIMD.int16x8.lessThan = function(t, other) { - t = SIMD.int16x8(t); - other = SIMD.int16x8(other); + t = SIMD.int16x8.check(t); + other = SIMD.int16x8.check(other); var cs0 = t.s0 < other.s0; var cs1 = t.s1 < other.s1; var cs2 = t.s2 < other.s2; @@ -3155,8 +3209,8 @@ if (typeof SIMD.int16x8.lessThanOrEqual === "undefined") { * the result of t <= other. */ SIMD.int16x8.lessThanOrEqual = function(t, other) { - t = SIMD.int16x8(t); - other = SIMD.int16x8(other); + t = SIMD.int16x8.check(t); + other = SIMD.int16x8.check(other); var cs0 = t.s0 <= other.s0; var cs1 = t.s1 <= other.s1; var cs2 = t.s2 <= other.s2; @@ -3176,9 +3230,7 @@ if (typeof SIMD.int16x8.shiftLeftByScalar === "undefined") { * @return {int16x8} lanes in a shifted by bits. */ SIMD.int16x8.shiftLeftByScalar = function(a, bits) { - a = SIMD.int16x8(a); - if (bits>>>0 > 16) - bits = 16; + a = SIMD.int16x8.check(a); var s0 = a.s0 << bits; var s1 = a.s1 << bits; var s2 = a.s2 << bits; @@ -3198,17 +3250,15 @@ if (typeof SIMD.int16x8.shiftRightLogicalByScalar === "undefined") { * @return {int16x8} lanes in a shifted by bits. */ SIMD.int16x8.shiftRightLogicalByScalar = function(a, bits) { - a = SIMD.int16x8(a); - if (bits>>>0 > 16) - bits = 16; - var s0 = a.s0 >>> bits; - var s1 = a.s1 >>> bits; - var s2 = a.s2 >>> bits; - var s3 = a.s3 >>> bits; - var s4 = a.s4 >>> bits; - var s5 = a.s5 >>> bits; - var s6 = a.s6 >>> bits; - var s7 = a.s7 >>> bits; + a = SIMD.int16x8.check(a); + var s0 = (a.s0 & 0xffff) >>> bits; + var s1 = (a.s1 & 0xffff) >>> bits; + var s2 = (a.s2 & 0xffff) >>> bits; + var s3 = (a.s3 & 0xffff) >>> bits; + var s4 = (a.s4 & 0xffff) >>> bits; + var s5 = (a.s5 & 0xffff) >>> bits; + var s6 = (a.s6 & 0xffff) >>> bits; + var s7 = (a.s7 & 0xffff) >>> bits; return SIMD.int16x8(s0, s1, s2, s3, s4, s5, s6, s7); } } @@ -3220,9 +3270,7 @@ if (typeof SIMD.int16x8.shiftRightArithmeticByScalar === "undefined") { * @return {int16x8} lanes in a shifted by bits. */ SIMD.int16x8.shiftRightArithmeticByScalar = function(a, bits) { - a = SIMD.int16x8(a); - if (bits>>>0 > 16) - bits = 16; + a = SIMD.int16x8.check(a); var s0 = a.s0 >> bits; var s1 = a.s1 >> bits; var s2 = a.s2 >> bits; @@ -3277,7 +3325,7 @@ if (typeof SIMD.int16x8.store === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 16) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.int16x8(value); + value = SIMD.int16x8.check(value); _SIMD_PRIVATE._i16x8[0] = value.s0; _SIMD_PRIVATE._i16x8[1] = value.s1; _SIMD_PRIVATE._i16x8[2] = value.s2; @@ -3303,8 +3351,8 @@ if (typeof SIMD.int8x16.and === "undefined") { * @return {int8x16} New instance of int8x16 with values of a & b. */ SIMD.int8x16.and = function(a, b) { - a = SIMD.int8x16(a); - b = SIMD.int8x16(b); + a = SIMD.int8x16.check(a); + b = SIMD.int8x16.check(b); return SIMD.int8x16(a.s0 & b.s0, a.s1 & b.s1, a.s2 & b.s2, a.s3 & b.s3, a.s4 & b.s4, a.s5 & b.s5, a.s6 & b.s6, a.s7 & b.s7, a.s8 & b.s8, a.s9 & b.s9, a.s10 & b.s10, a.s11 & b.s11, @@ -3319,8 +3367,8 @@ if (typeof SIMD.int8x16.or === "undefined") { * @return {int8x16} New instance of int8x16 with values of a | b. */ SIMD.int8x16.or = function(a, b) { - a = SIMD.int8x16(a); - b = SIMD.int8x16(b); + a = SIMD.int8x16.check(a); + b = SIMD.int8x16.check(b); return SIMD.int8x16(a.s0 | b.s0, a.s1 | b.s1, a.s2 | b.s2, a.s3 | b.s3, a.s4 | b.s4, a.s5 | b.s5, a.s6 | b.s6, a.s7 | b.s7, a.s8 | b.s8, a.s9 | b.s9, a.s10 | b.s10, a.s11 | b.s11, @@ -3335,8 +3383,8 @@ if (typeof SIMD.int8x16.xor === "undefined") { * @return {int8x16} New instance of int8x16 with values of a ^ b. */ SIMD.int8x16.xor = function(a, b) { - a = SIMD.int8x16(a); - b = SIMD.int8x16(b); + a = SIMD.int8x16.check(a); + b = SIMD.int8x16.check(b); return SIMD.int8x16(a.s0 ^ b.s0, a.s1 ^ b.s1, a.s2 ^ b.s2, a.s3 ^ b.s3, a.s4 ^ b.s4, a.s5 ^ b.s5, a.s6 ^ b.s6, a.s7 ^ b.s7, a.s8 ^ b.s8, a.s9 ^ b.s9, a.s10 ^ b.s10, a.s11 ^ b.s11, @@ -3350,7 +3398,7 @@ if (typeof SIMD.int8x16.not === "undefined") { * @return {int8x16} New instance of int8x16 with values of ~t */ SIMD.int8x16.not = function(t) { - t = SIMD.int8x16(t); + t = SIMD.int8x16.check(t); return SIMD.int8x16(~t.s0, ~t.s1, ~t.s2, ~t.s3, ~t.s4, ~t.s5, ~t.s6, ~t.s7, ~t.s8, ~t.s9, ~t.s10, ~t.s11, @@ -3364,7 +3412,7 @@ if (typeof SIMD.int8x16.neg === "undefined") { * @return {int8x16} New instance of int8x16 with values of -t */ SIMD.int8x16.neg = function(t) { - t = SIMD.int8x16(t); + t = SIMD.int8x16.check(t); return SIMD.int8x16(-t.s0, -t.s1, -t.s2, -t.s3, -t.s4, -t.s5, -t.s6, -t.s7, -t.s8, -t.s9, -t.s10, -t.s11, @@ -3379,8 +3427,8 @@ if (typeof SIMD.int8x16.add === "undefined") { * @return {int8x16} New instance of int8x16 with values of a + b. */ SIMD.int8x16.add = function(a, b) { - a = SIMD.int8x16(a); - b = SIMD.int8x16(b); + a = SIMD.int8x16.check(a); + b = SIMD.int8x16.check(b); return SIMD.int8x16(a.s0 + b.s0, a.s1 + b.s1, a.s2 + b.s2, a.s3 + b.s3, a.s4 + b.s4, a.s5 + b.s5, a.s6 + b.s6, a.s7 + b.s7, a.s8 + b.s8, a.s9 + b.s9, a.s10 + b.s10, a.s11 + b.s11, @@ -3395,8 +3443,8 @@ if (typeof SIMD.int8x16.sub === "undefined") { * @return {int8x16} New instance of int8x16 with values of a - b. */ SIMD.int8x16.sub = function(a, b) { - a = SIMD.int8x16(a); - b = SIMD.int8x16(b); + a = SIMD.int8x16.check(a); + b = SIMD.int8x16.check(b); return SIMD.int8x16(a.s0 - b.s0, a.s1 - b.s1, a.s2 - b.s2, a.s3 - b.s3, a.s4 - b.s4, a.s5 - b.s5, a.s6 - b.s6, a.s7 - b.s7, a.s8 - b.s8, a.s9 - b.s9, a.s10 - b.s10, a.s11 - b.s11, @@ -3411,8 +3459,8 @@ if (typeof SIMD.int8x16.mul === "undefined") { * @return {int8x16} New instance of int8x16 with values of a * b. */ SIMD.int8x16.mul = function(a, b) { - a = SIMD.int8x16(a); - b = SIMD.int8x16(b); + a = SIMD.int8x16.check(a); + b = SIMD.int8x16.check(b); return SIMD.int8x16(Math.imul(a.s0, b.s0), Math.imul(a.s1, b.s1), Math.imul(a.s2, b.s2), Math.imul(a.s3, b.s3), Math.imul(a.s4, b.s4), Math.imul(a.s5, b.s5), @@ -3435,9 +3483,42 @@ if (typeof SIMD.int8x16.select === "undefined") { * indicated */ SIMD.int8x16.select = function(t, trueValue, falseValue) { - t = SIMD.int8x16(t); - trueValue = SIMD.int8x16(trueValue); - falseValue = SIMD.int8x16(falseValue); + t = SIMD.int8x16.check(t); + trueValue = SIMD.int8x16.check(trueValue); + falseValue = SIMD.int8x16.check(falseValue); + return SIMD.int8x16(_SIMD_PRIVATE.tobool(t.s0) ? trueValue.s0 : falseValue.s0, + _SIMD_PRIVATE.tobool(t.s1) ? trueValue.s1 : falseValue.s1, + _SIMD_PRIVATE.tobool(t.s2) ? trueValue.s2 : falseValue.s2, + _SIMD_PRIVATE.tobool(t.s3) ? trueValue.s3 : falseValue.s3, + _SIMD_PRIVATE.tobool(t.s4) ? trueValue.s4 : falseValue.s4, + _SIMD_PRIVATE.tobool(t.s5) ? trueValue.s5 : falseValue.s5, + _SIMD_PRIVATE.tobool(t.s6) ? trueValue.s6 : falseValue.s6, + _SIMD_PRIVATE.tobool(t.s7) ? trueValue.s7 : falseValue.s7, + _SIMD_PRIVATE.tobool(t.s8) ? trueValue.s8 : falseValue.s8, + _SIMD_PRIVATE.tobool(t.s9) ? trueValue.s9 : falseValue.s9, + _SIMD_PRIVATE.tobool(t.s10) ? trueValue.s10 : falseValue.s10, + _SIMD_PRIVATE.tobool(t.s11) ? trueValue.s11 : falseValue.s11, + _SIMD_PRIVATE.tobool(t.s12) ? trueValue.s12 : falseValue.s12, + _SIMD_PRIVATE.tobool(t.s13) ? trueValue.s13 : falseValue.s13, + _SIMD_PRIVATE.tobool(t.s14) ? trueValue.s14 : falseValue.s14, + _SIMD_PRIVATE.tobool(t.s15) ? trueValue.s15 : falseValue.s15); + } +} + +if (typeof SIMD.int8x16.bitselect === "undefined") { + /** + * @param {int8x16} t Selector mask. An instance of int8x16 + * @param {int8x16} trueValue Pick lane from here if corresponding + * selector bit is 1 + * @param {int8x16} falseValue Pick lane from here if corresponding + * selector bit is 0 + * @return {int8x16} Mix of lanes from trueValue or falseValue as + * indicated + */ + SIMD.int8x16.bitselect = function(t, trueValue, falseValue) { + t = SIMD.int8x16.check(t); + trueValue = SIMD.int8x16.check(trueValue); + falseValue = SIMD.int8x16.check(falseValue); var tr = SIMD.int8x16.and(t, trueValue); var fr = SIMD.int8x16.and(SIMD.int8x16.not(t), falseValue); return SIMD.int8x16.or(tr, fr); @@ -3452,8 +3533,8 @@ if (typeof SIMD.int8x16.equal === "undefined") { * the result of t == other. */ SIMD.int8x16.equal = function(t, other) { - t = SIMD.int8x16(t); - other = SIMD.int8x16(other); + t = SIMD.int8x16.check(t); + other = SIMD.int8x16.check(other); var cs0 = t.s0 == other.s0; var cs1 = t.s1 == other.s1; var cs2 = t.s2 == other.s2; @@ -3483,8 +3564,8 @@ if (typeof SIMD.int8x16.notEqual === "undefined") { * the result of t != other. */ SIMD.int8x16.notEqual = function(t, other) { - t = SIMD.int8x16(t); - other = SIMD.int8x16(other); + t = SIMD.int8x16.check(t); + other = SIMD.int8x16.check(other); var cs0 = t.s0 != other.s0; var cs1 = t.s1 != other.s1; var cs2 = t.s2 != other.s2; @@ -3514,8 +3595,8 @@ if (typeof SIMD.int8x16.greaterThan === "undefined") { * the result of t > other. */ SIMD.int8x16.greaterThan = function(t, other) { - t = SIMD.int8x16(t); - other = SIMD.int8x16(other); + t = SIMD.int8x16.check(t); + other = SIMD.int8x16.check(other); var cs0 = t.s0 > other.s0; var cs1 = t.s1 > other.s1; var cs2 = t.s2 > other.s2; @@ -3545,8 +3626,8 @@ if (typeof SIMD.int8x16.greaterThanOrEqual === "undefined") { * the result of t >= other. */ SIMD.int8x16.greaterThanOrEqual = function(t, other) { - t = SIMD.int8x16(t); - other = SIMD.int8x16(other); + t = SIMD.int8x16.check(t); + other = SIMD.int8x16.check(other); var cs0 = t.s0 >= other.s0; var cs1 = t.s1 >= other.s1; var cs2 = t.s2 >= other.s2; @@ -3576,8 +3657,8 @@ if (typeof SIMD.int8x16.lessThan === "undefined") { * the result of t < other. */ SIMD.int8x16.lessThan = function(t, other) { - t = SIMD.int8x16(t); - other = SIMD.int8x16(other); + t = SIMD.int8x16.check(t); + other = SIMD.int8x16.check(other); var cs0 = t.s0 < other.s0; var cs1 = t.s1 < other.s1; var cs2 = t.s2 < other.s2; @@ -3607,8 +3688,8 @@ if (typeof SIMD.int8x16.lessThanOrEqual === "undefined") { * the result of t <= other. */ SIMD.int8x16.lessThanOrEqual = function(t, other) { - t = SIMD.int8x16(t); - other = SIMD.int8x16(other); + t = SIMD.int8x16.check(t); + other = SIMD.int8x16.check(other); var cs0 = t.s0 <= other.s0; var cs1 = t.s1 <= other.s1; var cs2 = t.s2 <= other.s2; @@ -3637,9 +3718,7 @@ if (typeof SIMD.int8x16.shiftLeftByScalar === "undefined") { * @return {int8x16} lanes in a shifted by bits. */ SIMD.int8x16.shiftLeftByScalar = function(a, bits) { - a = SIMD.int8x16(a); - if (bits>>>0 > 8) - bits = 8; + a = SIMD.int8x16.check(a); var s0 = a.s0 << bits; var s1 = a.s1 << bits; var s2 = a.s2 << bits; @@ -3668,25 +3747,23 @@ if (typeof SIMD.int8x16.shiftRightLogicalByScalar === "undefined") { * @return {int8x16} lanes in a shifted by bits. */ SIMD.int8x16.shiftRightLogicalByScalar = function(a, bits) { - a = SIMD.int8x16(a); - if (bits>>>0 > 8) - bits = 8; - var s0 = a.s0 >>> bits; - var s1 = a.s1 >>> bits; - var s2 = a.s2 >>> bits; - var s3 = a.s3 >>> bits; - var s4 = a.s4 >>> bits; - var s5 = a.s5 >>> bits; - var s6 = a.s6 >>> bits; - var s7 = a.s7 >>> bits; - var s8 = t.s8 >>> bits; - var s9 = t.s9 >>> bits; - var s10 = t.s10 >>> bits; - var s11 = t.s11 >>> bits; - var s12 = t.s12 >>> bits; - var s13 = t.s13 >>> bits; - var s14 = t.s14 >>> bits; - var s15 = t.s15 >>> bits; + a = SIMD.int8x16.check(a); + var s0 = (a.s0 & 0xff) >>> bits; + var s1 = (a.s1 & 0xff) >>> bits; + var s2 = (a.s2 & 0xff) >>> bits; + var s3 = (a.s3 & 0xff) >>> bits; + var s4 = (a.s4 & 0xff) >>> bits; + var s5 = (a.s5 & 0xff) >>> bits; + var s6 = (a.s6 & 0xff) >>> bits; + var s7 = (a.s7 & 0xff) >>> bits; + var s8 = (a.s8 & 0xff) >>> bits; + var s9 = (a.s9 & 0xff) >>> bits; + var s10 = (a.s10 & 0xff) >>> bits; + var s11 = (a.s11 & 0xff) >>> bits; + var s12 = (a.s12 & 0xff) >>> bits; + var s13 = (a.s13 & 0xff) >>> bits; + var s14 = (a.s14 & 0xff) >>> bits; + var s15 = (a.s15 & 0xff) >>> bits; return SIMD.int8x16(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); } @@ -3699,9 +3776,7 @@ if (typeof SIMD.int8x16.shiftRightArithmeticByScalar === "undefined") { * @return {int8x16} lanes in a shifted by bits. */ SIMD.int8x16.shiftRightArithmeticByScalar = function(a, bits) { - a = SIMD.int8x16(a); - if (bits>>>0 > 8) - bits = 8; + a = SIMD.int8x16.check(a); var s0 = a.s0 >> bits; var s1 = a.s1 >> bits; var s2 = a.s2 >> bits; @@ -3710,14 +3785,14 @@ if (typeof SIMD.int8x16.shiftRightArithmeticByScalar === "undefined") { var s5 = a.s5 >> bits; var s6 = a.s6 >> bits; var s7 = a.s7 >> bits; - var s8 = t.s8 >> bits; - var s9 = t.s9 >> bits; - var s10 = t.s10 >> bits; - var s11 = t.s11 >> bits; - var s12 = t.s12 >> bits; - var s13 = t.s13 >> bits; - var s14 = t.s14 >> bits; - var s15 = t.s15 >> bits; + var s8 = a.s8 >> bits; + var s9 = a.s9 >> bits; + var s10 = a.s10 >> bits; + var s11 = a.s11 >> bits; + var s12 = a.s12 >> bits; + var s13 = a.s13 >> bits; + var s14 = a.s14 >> bits; + var s15 = a.s15 >> bits; return SIMD.int8x16(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); } @@ -3767,7 +3842,7 @@ if (typeof SIMD.int8x16.store === "undefined") { var bpe = tarray.BYTES_PER_ELEMENT; if (index < 0 || (index * bpe + 16) > tarray.byteLength) throw new RangeError("The value of index is invalid."); - value = SIMD.int8x16(value); + value = SIMD.int8x16.check(value); _SIMD_PRIVATE._i8x16[0] = value.s0; _SIMD_PRIVATE._i8x16[1] = value.s1; _SIMD_PRIVATE._i8x16[2] = value.s2; @@ -4065,7 +4140,7 @@ if (typeof Int32x4Array === "undefined") { throw new TypeError("This is not a DataView."); if (byteOffset < 0 || (byteOffset + 16) > this.buffer.byteLength) throw new RangeError("The value of byteOffset is invalid."); - value = SIMD.float32x4(value); + value = SIMD.float32x4.check(value); if (typeof littleEndian === 'undefined') littleEndian = false; this.setFloat32(byteOffset, value.x, littleEndian); @@ -4079,7 +4154,7 @@ if (typeof Int32x4Array === "undefined") { throw new TypeError("This is not a DataView."); if (byteOffset < 0 || (byteOffset + 16) > this.buffer.byteLength) throw new RangeError("The value of byteOffset is invalid."); - value = SIMD.float64x2(value); + value = SIMD.float64x2.check(value); if (typeof littleEndian === 'undefined') littleEndian = false; this.setFloat64(byteOffset, value.x, littleEndian); @@ -4091,7 +4166,7 @@ if (typeof Int32x4Array === "undefined") { throw new TypeError("This is not a DataView."); if (byteOffset < 0 || (byteOffset + 16) > this.buffer.byteLength) throw new RangeError("The value of byteOffset is invalid."); - value = SIMD.int32x4(value); + value = SIMD.int32x4.check(value); if (typeof littleEndian === 'undefined') littleEndian = false; this.setInt32(byteOffset, value.x, littleEndian); @@ -4105,7 +4180,7 @@ if (typeof Int32x4Array === "undefined") { throw new TypeError("This is not a DataView."); if (byteOffset < 0 || (byteOffset + 16) > this.buffer.byteLength) throw new RangeError("The value of byteOffset is invalid."); - value = SIMD.int16x8(value); + value = SIMD.int16x8.check(value); if (typeof littleEndian === 'undefined') littleEndian = false; this.setInt16(byteOffset, value.s0, littleEndian); @@ -4123,7 +4198,7 @@ if (typeof Int32x4Array === "undefined") { throw new TypeError("This is not a DataView."); if (byteOffset < 0 || (byteOffset + 16) > this.buffer.byteLength) throw new RangeError("The value of byteOffset is invalid."); - value = SIMD.int8x16(value); + value = SIMD.int8x16.check(value); if (typeof littleEndian === 'undefined') littleEndian = false; this.setInt8(byteOffset, value.s0, littleEndian); From 7d104e16456288dc9a2dec624a9ef1af043bac16 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 10:23:00 -0800 Subject: [PATCH 44/76] fix EMCONFIGURE_JS check that was broken in 9be0275876217eacd30c848b1c7cea68d1a3d986 --- emcc | 2 +- tests/test_other.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/emcc b/emcc index 57d5699554997..2fe1e83add8ed 100755 --- a/emcc +++ b/emcc @@ -190,7 +190,7 @@ if CONFIGURE_CONFIG or CMAKE_CONFIG: # whether we fake configure tests using clang - the local, native compiler - or not. if not we generate JS and use node with a shebang # neither approach is perfect, you can try both, but may need to edit configure scripts in some cases # by default we configure in js, which can break on local filesystem access, etc., but is otherwise accurate - use_js = os.environ.get('EMCONFIGURE_JS') or 1 + use_js = int(os.environ.get('EMCONFIGURE_JS') or 1) if debug_configure: tempout = '/tmp/emscripten_temp/out' diff --git a/tests/test_other.py b/tests/test_other.py index d033ac69c4cce..ed958cba357ed 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4643,9 +4643,13 @@ def test_emconfigure_js_o(self): print i try: os.environ['EMCONFIGURE_JS'] = str(i) + self.clear() Popen([PYTHON, path_from_root('emconfigure'), PYTHON, EMCC, '-c', '-o', 'a.o', path_from_root('tests', 'hello_world.c')]).communicate() Popen([PYTHON, EMCC, 'a.o']).communicate() - assert 'hello, world!' in run_js(self.in_dir('a.out.js')) + if i == 0: + assert not os.path.exists('a.out.js') # native .o, not bitcode! + else: + assert 'hello, world!' in run_js(self.in_dir('a.out.js')) finally: del os.environ['EMCONFIGURE_JS'] From 143fd420ef2640c6bec3e0477e1d6a3659b8695a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 14:19:59 -0800 Subject: [PATCH 45/76] fix fround coercions on calls from emterpreter outside to function tables --- tools/emterpretify.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index b6da36f538d31..0cfdc6005046b 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -525,12 +525,24 @@ def make_target_call(i): function_pointer_call = name.startswith('FUNCTION_TABLE_') + # our local registers are never true floats, and we just do fround calls to ensure correctness, not caring + # about performance. but when coercing to outside of the emterpreter, we need to know the true sig, + # and must use frounds + true_sig = sig + if function_pointer_call: + true_sig = name.split('_')[-1] + + def fix_coercion(value, s): + if s == 'f': + value = 'Math_fround(' + value + ')' + return value + ret = name if function_pointer_call: ret += '[' + get_access('HEAPU8[pc+4>>0]') + ' & %d]' % (next_power_of_two(asm.tables[name].count(',')+1)-1) - ret += '(' + ', '.join([get_coerced_access('HEAPU8[pc+%d>>0]' % (i+4+int(function_pointer_call)), s=sig[i+1]) for i in range(len(sig)-1)]) + ')' + ret += '(' + ', '.join([fix_coercion(get_coerced_access('HEAPU8[pc+%d>>0]' % (i+4+int(function_pointer_call)), s=sig[i+1]), true_sig[i+1]) for i in range(len(sig)-1)]) + ')' if sig[0] != 'v': - ret = ' = ' + shared.JS.make_coercion(ret, sig[0]) + ret = ' = ' + shared.JS.make_coercion(fix_coercion(ret, true_sig[0]), sig[0]) if not ASYNC: ret = get_access('lx', sig[0]) + ret else: From 193c659de3b79b6ab810d89140f07762d404021d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 14:32:48 -0800 Subject: [PATCH 46/76] send entire signatures to emterpreter --- tools/emterpretify.py | 17 +++++------------ tools/js-optimizer.js | 10 +++++++++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 0cfdc6005046b..0c30958b6f3e4 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -552,8 +552,8 @@ def fix_coercion(value, s): else: assert sig[0] == 'd' ret = 'ld ' + ret - elif name in actual_return_types and actual_return_types[name] != 'v': - ret = shared.JS.make_coercion(ret, actual_return_types[name]) # return value ignored, but need a coercion + elif name in actual_sigs and actual_sigs[name][0] != 'v': + ret = shared.JS.make_coercion(ret, actual_sigs[name][0]) # return value ignored, but need a coercion if ASYNC: # check if we are asyncing, and if not, it is ok to save the return value ret = handle_async_pre_call() + ret + '; ' + handle_async_post_call() @@ -879,7 +879,7 @@ def process_code(func, code, absolute_targets): for k in range(4): code[j + k] = value[k] - actual_return_types = {} + actual_sigs = {} for i in range(len(lines)): line = lines[i] @@ -900,15 +900,8 @@ def process_code(func, code, absolute_targets): func = None lines[i] = '' elif line.startswith('// return type: ['): - name, ret = line.split('[')[1].split(']')[0].split(',') - if ret == 'undefined': - actual_return_types[name] = 'v' - elif ret == '0': - actual_return_types[name] = 'i' - elif ret == '1': - actual_return_types[name] = 'd' - elif ret == '2': - actual_return_types[name] = 'f' + name, sig = line.split('[')[1].split(']')[0].split(',') + actual_sigs[name] = sig lines[i] = '' assert global_func_id < 65536, [global_funcs, global_func_id] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 0134770377566..4dea730763865 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2341,6 +2341,14 @@ function getCombinedSign(node1, node2, hint) { assert(0, JSON.stringify([node1, ' ', node2, sign1, sign2, hint])); } +function getSignature(func, asmData) { + var ret = asmData.ret >= 0 ? ASM_SIG[asmData.ret] : 'v'; + for (var i = 0; i < func[2].length; i++) { + ret += ASM_SIG[asmData.params[func[2][i]]]; + } + return ret; +} + function normalizeAsm(func) { //printErr('pre-normalize \n\n' + astToSrc(func) + '\n\n'); var data = { @@ -7490,7 +7498,7 @@ function emterpretify(ast) { } var asmData = normalizeAsm(func); - print('// return type: [' + func[1] + ',' + asmData.ret + ']'); + print('// return type: [' + func[1] + ',' + getSignature(func, asmData) + ']'); if (ignore) { return; From 5613f0bba1bb395959c128eadb17ca3c1d18e494 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 14:59:35 -0800 Subject: [PATCH 47/76] fix fround coercions on calls from emterpreter outside to other functions --- tools/emterpretify.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 0c30958b6f3e4..655e730067dd0 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -531,6 +531,8 @@ def make_target_call(i): true_sig = sig if function_pointer_call: true_sig = name.split('_')[-1] + elif name in actual_sigs: + true_sig = actual_sigs[name] def fix_coercion(value, s): if s == 'f': From e4b65c4369af06b5ba24952ebd103bd65967f7bd Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 15:13:24 -0800 Subject: [PATCH 48/76] parse global non-zero constants in asm_module --- tools/asm_module.py | 18 ++++++++++++++++-- tools/emterpretify.py | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tools/asm_module.py b/tools/asm_module.py index 8a5ccf56b5490..cb6468327fe82 100644 --- a/tools/asm_module.py +++ b/tools/asm_module.py @@ -316,9 +316,23 @@ def apply_funcs_map(self, funcs_map): # assumes self.funcs is the set of funcs, self.funcs_js = '\n'.join(jses) def get_import_type(self, imp): - if '|0' in imp or '| 0' in imp or imp == '0': + def is_int(x): + try: + int(x) + return True + except: + return False + + def is_float(x): + try: + float(x) + return True + except: + return False + + if '|0' in imp or '| 0' in imp or (is_int(imp) and not '.0' in imp or '+' in imp): return 'i' - elif '.0' in imp or '+' in imp: + elif '.0' in imp or '+' in imp or is_float(imp): return 'd' else: return '?' diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 655e730067dd0..b6ca4212ee883 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -820,7 +820,7 @@ def note_global(target, j, code): global global_var_id imp = asm.imports[target] ty = asm.get_import_type(imp) - assert ty in ['i', 'd'] + assert ty in ['i', 'd'], target if code[j] == 'GETGLBI' and ty == 'd': # the js optimizer doesn't know all types, we must fix it up here assert '.0' in imp or '+' in imp, imp From cd9f3e42f3b7e29dd875301262265599507082ec Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 18:02:03 -0800 Subject: [PATCH 49/76] clean up temp files in emterpreter --- tools/emterpretify.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index b6ca4212ee883..9959f49d0b354 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -977,3 +977,5 @@ def post_process_code(code): asm.write(outfile) +temp_files.clean() + From 6caec783c4beb0d9df2cbfcc758beed2ba3fdda8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 18:02:18 -0800 Subject: [PATCH 50/76] clean up js optimizer output temp files in emcc --- emcc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/emcc b/emcc index 2fe1e83add8ed..14074443499b1 100755 --- a/emcc +++ b/emcc @@ -388,6 +388,8 @@ def log_time(name): logging.debug('emcc step "%s" took %.2f seconds', name, now - log_time_last) log_time_last = now +misc_temp_files = shared.configuration.get_temp_files() + try: call = CXX if use_cxx else CC @@ -1430,7 +1432,9 @@ try: if minify_whitespace and 'last' in passes: passes += ['minifyWhitespace'] logging.debug('applying js optimization passes: %s', ' '.join(passes)) + misc_temp_files.note(final) final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4, js_optimizer_extra_info, just_split=just_split, just_concat=just_concat) + misc_temp_files.note(final) js_transform_tempfiles.append(final) if DEBUG: save_intermediate(title, suffix='js' if 'emitJSON' not in passes else 'json') @@ -1818,4 +1822,5 @@ finally: pass else: logging.info('emcc saved files are in:' + temp_dir) + misc_temp_files.clean() From 4c996f01e16803731dfbeb56a2c9773d6974f589 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 18:17:17 -0800 Subject: [PATCH 51/76] fix emterpreter asm validation under closure, and add testing --- tests/test_core.py | 2 +- tools/emterpretify.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index a734e70b17bec..7366b27b88cd2 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6058,7 +6058,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: + if self.emcc_args is not None and ('-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) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 9959f49d0b354..fcfa28a7da6f8 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -972,7 +972,7 @@ def post_process_code(code): asm.set_pre_js(js='var EMTSTACKTOP = STATIC_BASE + %s, EMT_STACK_MAX = EMTSTACKTOP + %d;' % (stack_start, EMT_STACK_MAX)) # send EMT vars into asm - asm.pre_js += 'Module.asmLibraryArg.EMTSTACKTOP = EMTSTACKTOP; Module.asmLibraryArg.EMT_STACK_MAX = EMT_STACK_MAX;\n' + asm.pre_js += "Module.asmLibraryArg['EMTSTACKTOP'] = EMTSTACKTOP; Module.asmLibraryArg['EMT_STACK_MAX'] = EMT_STACK_MAX;\n" asm.imports_js += 'var EMTSTACKTOP = env.EMTSTACKTOP|0;\nvar EMT_STACK_MAX = env.EMT_STACK_MAX|0;\n' asm.write(outfile) From 4a4bc6046f46e593d649f22d73caa1fca2921682 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 18:19:21 -0800 Subject: [PATCH 52/76] add testing for emterpreter calls to float function tables --- tests/test_core.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 7366b27b88cd2..32c8361f2b70b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1863,6 +1863,11 @@ def test_mathfuncptr(self): self.do_run_from_file(src, output) + if self.is_emterpreter(): + print 'emterpreter f32' + Settings.PRECISE_F32 = 1 + self.do_run_from_file(src, output) + def test_funcptrfunc(self): test_path = path_from_root('tests', 'core', 'test_funcptrfunc') src, output = (test_path + s for s in ('.in', '.out')) From 56c46feb93352a7fb00c3693e3a92de77eeb8b29 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 18:20:50 -0800 Subject: [PATCH 53/76] test lua in float32 in emterpreter --- tests/test_core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 32c8361f2b70b..33c2fd8fc37c3 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5414,6 +5414,9 @@ def test_lua(self): total_memory = Settings.TOTAL_MEMORY + if self.is_emterpreter(): + Settings.PRECISE_F32 = 1 + for aggro in ([0, 1] if Settings.ASM_JS and '-O2' in self.emcc_args else [0]): for masking in ([0, 1] if Settings.ASM_JS and '-O2' in self.emcc_args and os.environ.get('EMCC_FAST_COMPILER') != '0' else [0]): Settings.AGGRESSIVE_VARIABLE_ELIMINATION = aggro From f36abe1b0f31e91c5d7ddfbbb7ca5b9c3b335a1d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 19:51:32 -0800 Subject: [PATCH 54/76] handle flexible signing in / and % in emterpreter, can use signed --- tests/test_other.py | 6 ++++++ tools/js-optimizer.js | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_other.py b/tests/test_other.py index ed958cba357ed..d85e354c971dd 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4539,6 +4539,12 @@ def get_func(src, name): } ''', [], '123\n') + do_js_test('flexible mod', r''' +function _main() { + print(1 % 16); +} +''', [], '1\n') + # codegen log tests def do_log_test(source, expected, func): diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 4dea730763865..4aab815ef9d5a 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6821,7 +6821,7 @@ function emterpretify(ast) { } case '/': { if (type === ASM_INT) { - assert(sign !== ASM_FLEXIBLE); + if (sign === ASM_FLEXIBLE) sign = ASM_SIGNED; if (sign === ASM_SIGNED) opcode = 'SDIV'; else opcode = 'UDIV'; tryNumAsymmetrical(sign === ASM_UNSIGNED); @@ -6831,7 +6831,7 @@ function emterpretify(ast) { } case '%': { if (type === ASM_INT) { - assert(sign !== ASM_FLEXIBLE); + if (sign === ASM_FLEXIBLE) sign = ASM_SIGNED; if (sign === ASM_SIGNED) opcode = 'SMOD'; else opcode = 'UMOD'; tryNumAsymmetrical(sign === ASM_UNSIGNED); From 062d80a5a9dec12db8784e631a23372d649470f8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 14 Feb 2015 20:43:14 -0800 Subject: [PATCH 55/76] error on closure 2 with emterpreter --- emcc | 1 + 1 file changed, 1 insertion(+) diff --git a/emcc b/emcc index 14074443499b1..53580c7a71d8e 100755 --- a/emcc +++ b/emcc @@ -1052,6 +1052,7 @@ try: if not memory_init_file: logging.warning('enabling --memory-init-file for EMTERPRETIFY') memory_init_file = True + assert closure is not 2, 'EMTERPRETIFY requires valid asm.js, and is incompatible with closure 2 which disables that' if proxy_to_worker: shared.Settings.PROXY_TO_WORKER = 1 From a3bac6b0bfcbe962ccadfb2d462901c90c41cd56 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 15 Feb 2015 11:25:27 -0800 Subject: [PATCH 56/76] fix and test emterpreter on asyncify output --- src/library_async.js | 2 +- tests/test_browser.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/library_async.js b/src/library_async.js index cc04153f1e478..28d83bd1f4abe 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -95,7 +95,7 @@ mergeInto(LibraryManager.library, { #if ASSERTIONS assert((((___async_cur_frame + 8)|0) == (ctx|0))|0); #endif - stackRestore(___async_cur_frame); + stackRestore(___async_cur_frame | 0); ___async_cur_frame = {{{ makeGetValueAsm('___async_cur_frame', 0, 'i32') }}}; }, diff --git a/tests/test_browser.py b/tests/test_browser.py index 9c9030d099e03..cfc1cbcfcf6cf 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2077,6 +2077,8 @@ def test_wget(self): with open(os.path.join(self.get_dir(), 'test.txt'), 'w') as f: f.write('emscripten') self.btest(path_from_root('tests', 'test_wget.c'), expected='1', args=['-s', 'ASYNCIFY=1']) + print 'asyncify+emterpreter' + self.btest(path_from_root('tests', 'test_wget.c'), expected='1', args=['-s', 'ASYNCIFY=1', '-s', 'EMTERPRETIFY=1']) def test_wget_data(self): with open(os.path.join(self.get_dir(), 'test.txt'), 'w') as f: From e3aab920a51f92c5b1d3b32b77e8d9bbf6087b63 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 15 Feb 2015 12:32:01 -0800 Subject: [PATCH 57/76] another asyncify cleanup for emterpreter --- src/library_async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library_async.js b/src/library_async.js index 28d83bd1f4abe..b479399a69425 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -81,7 +81,7 @@ mergeInto(LibraryManager.library, { emscripten_realloc_async_context: function(len) { len = len|0; // assuming that we have on the stacktop - stackRestore(___async_cur_frame); + stackRestore(___async_cur_frame | 0); return ((stackAlloc((len + 8)|0)|0) + 8)|0; }, From 82b35c52d608229c00a51ee9956ace60dcfcd451 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 15 Feb 2015 13:48:57 -0800 Subject: [PATCH 58/76] do not try to find uninitialized vars if it would take too long and provide little benefit anyhow --- tools/js-optimizer.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 4aab815ef9d5a..2996205c60454 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -7541,6 +7541,7 @@ function emterpretify(ast) { var locals = {}; var numLocals = 0; // ignores slow locals, they are over 255 and not directly accessible + var numVars = 0; function countLocals() { numLocals = 0; @@ -7549,6 +7550,7 @@ function emterpretify(ast) { } for (var i in asmData.vars) { numLocals++; + numVars++; } } @@ -7567,8 +7569,13 @@ function emterpretify(ast) { locals[i] = numLocals++; } assert(numLocals <= FAST_LOCALS, 'way too many params!'); - assert(FAST_LOCALS < 256); // sanity - var zeroInits = findUninitializedVars(func, asmData); + assert(FAST_LOCALS < 256); + var zeroInits; + if (numVars < FAST_LOCALS*2) { + zeroInits = findUninitializedVars(func, asmData); + } else { + zeroInits = copy(asmData.vars); // give up - tons of vars, this will be slow anyhow, and findUninitializedVars is non-linear so can be very slow on massive function + } var numZeroInits = 0; for (var zero in zeroInits) { locals[zero] = numLocals++; From 34e7cda54bdf64da5b46e64e2845529d8a6f7ab9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 15 Feb 2015 18:36:37 -0800 Subject: [PATCH 59/76] enable test_coroutine in emterpreter --- tests/test_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 33c2fd8fc37c3..e7e6207c50cf6 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7241,7 +7241,6 @@ def test_async(self): def test_coroutine(self): if not Settings.ASM_JS: return self.skip('asyncify requires asm.js') if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('asyncify requires fastcomp') - if self.is_emterpreter(): return self.skip('todo') src = r''' #include From 352e7b6f3d226b57a7f3750f9e20b55579a78226 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Sun, 15 Feb 2015 20:42:06 -0600 Subject: [PATCH 60/76] add Luke Wagner to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 69f84c6ca8588..6e909b415b98d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -171,4 +171,5 @@ a license to everyone to use it as detailed in LICENSE.) * Petr Babicka * Akira Takahashi * Victor Costan +* Luke Wagner (copyright owned by Mozilla Foundation) From 53ccd6c2130ab8524603c5fcb646b10ec2277293 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 15 Feb 2015 21:00:47 -0800 Subject: [PATCH 61/76] js optimizer pass to dump the callgraph, and code in emterpretify to reverse it and prepare to advise on what should be emterpreted --- tools/emterpretify.py | 40 +++++++++++++++++++++++++++++++++++++++- tools/js-optimizer.js | 21 +++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index fcfa28a7da6f8..ddfe7efe6c249 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -23,9 +23,10 @@ PROFILING = False SWAPPABLE = False FROUND = False +ADVISE = False def handle_arg(arg): - global ZERO, ASYNC, ASSERTIONS, PROFILING, FROUND + global ZERO, ASYNC, ASSERTIONS, PROFILING, FROUND, ADVISE if '=' in arg: l, r = arg.split('=') if l == 'ZERO': ZERO = int(r) @@ -33,6 +34,7 @@ def handle_arg(arg): elif l == 'ASSERTIONS': ASSERTIONS = int(r) elif l == 'PROFILING': PROFILING = int(r) elif l == 'FROUND': FROUND = int(r) + elif l == 'ADVISE': ADVISE = int(r) return False return True @@ -54,6 +56,8 @@ def handle_arg(arg): BLACKLIST = set(['_malloc', '_free', '_memcpy', '_memmove', '_memset', 'copyTempDouble', 'copyTempFloat', '_strlen', 'stackAlloc', 'setThrew', 'stackRestore', 'setTempRet0', 'getTempRet0', 'stackSave', 'runPostSets', '_emscripten_autodebug_double', '_emscripten_autodebug_float', '_emscripten_autodebug_i8', '_emscripten_autodebug_i16', '_emscripten_autodebug_i32', '_emscripten_autodebug_i64', '_strncpy', '_strcpy', '_strcat', '_saveSetjmp', '_testSetjmp', '_emscripten_replace_memory', '_bitshift64Shl', '_bitshift64Ashr', '_bitshift64Lshr', 'setAsyncState', 'emtStackSave']) WHITELIST = [] +SYNC_FUNCS = set(['_emscripten_sleep', '_emscripten_sleep_with_yield', '_emscripten_wget_data', '_emscripten_idb_load', '_emscripten_idb_store', '_emscripten_idb_delete']) + OPCODES = [ # l, lx, ly etc - one of 256 locals 'SET', # [lx, ly, 0] lx = ly (int or float, not double) 'SETVI', # [l, vl, vh] l = v (16-bit signed int) @@ -717,6 +721,40 @@ def process(code): if len(sys.argv) >= 7: SWAPPABLE = int(sys.argv[6]) + if ADVISE: + temp = temp_files.get('.js').name + shared.Building.js_optimizer(infile, ['dumpCallGraph'], output_filename=temp, just_concat=True) + asm = asm_module.AsmModule(temp) + lines = asm.funcs_js.split('\n') + can_call = {} + for i in range(len(lines)): + line = lines[i] + if line.startswith('// REACHABLE '): + curr = json.loads(line[len('// REACHABLE '):]) + func = curr[0] + targets = curr[2] + can_call[func] = set(targets) + print 'can call', can_call + reachable_from = {} + for func, targets in can_call.iteritems(): + for target in targets: + if target not in reachable_from: + reachable_from[target] = set() + reachable_from[target].add(func) + print 'reachable from', reachable_from + # find all functions that can reach the sync funcs, which are those that can be on the stack during an async save/load, and hence must all be emterpreted + to_check = list(SYNC_FUNCS) + advised = set() + while len(to_check) > 0: + curr = to_check.pop() + if curr in reachable_from: + for reacher in reachable_from[curr]: + if reacher not in advised: + advised.add(str(reacher)) + to_check.append(reacher) + print "-s EMTERPRETIFY_ASYNC_WHITELIST='" + str(sorted(list(advised))).replace("'", '"') + "'" + sys.exit(0) + BLACKLIST = set(list(BLACKLIST) + extra_blacklist) if DEBUG or SWAPPABLE: diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 2996205c60454..89167ebde7bad 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -7716,6 +7716,26 @@ function findReachable(ast) { print('// REACHABLE ' + JSON.stringify(keys(reachable))); } +// emits call graph information +function dumpCallGraph(ast) { + traverseGeneratedFunctions(ast, function(func) { + var reachable = {}; + traverse(func, function(node, type) { + if (type === 'call') { + if (node[1][0] === 'name') { + reachable[node[1][1]] = 1; + } else { + // (FUNCTION_TABLE[..])(..) + assert(node[1][0] === 'sub') + assert(node[1][1][0] === 'name'); + reachable[node[1][1][1]] = 1; + } + } + }); + print('// REACHABLE ' + JSON.stringify([func[1], ' => ', keys(reachable)])); + }); +} + // Last pass utilities // Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly) @@ -7873,6 +7893,7 @@ var passes = { ensureLabelSet: ensureLabelSet, emterpretify: emterpretify, findReachable: findReachable, + dumpCallGraph: dumpCallGraph, asmLastOpts: asmLastOpts, noop: function() {}, From ed278574eb943f5001b3892dbb577dc646d18387 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 16 Feb 2015 10:01:29 -0800 Subject: [PATCH 62/76] EMTERPRETIFY_ADVISE and test --- emcc | 6 ++++++ src/settings.js | 4 ++++ tests/emterpreter_advise.cpp | 38 ++++++++++++++++++++++++++++++++++++ tests/test_other.py | 4 ++++ tools/emterpretify.py | 11 ++++++++--- 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 tests/emterpreter_advise.cpp diff --git a/emcc b/emcc index 53580c7a71d8e..19d17f24c23ff 100755 --- a/emcc +++ b/emcc @@ -1595,6 +1595,8 @@ try: args = [shared.PYTHON, shared.path_from_root('tools', 'emterpretify.py'), js_target, final + '.em.js', memfile, json.dumps(shared.Settings.EMTERPRETIFY_BLACKLIST), json.dumps(shared.Settings.EMTERPRETIFY_WHITELIST), str(shared.Settings.SWAPPABLE_ASM_MODULE)] if shared.Settings.EMTERPRETIFY_ASYNC: args += ['ASYNC=1'] + if shared.Settings.EMTERPRETIFY_ADVISE: + args += ['ADVISE=1'] if profiling or profiling_funcs: args += ['PROFILING=1'] if shared.Settings.ASSERTIONS: @@ -1606,6 +1608,10 @@ try: finally: shared.try_delete(js_target) + if shared.Settings.EMTERPRETIFY_ADVISE: + logging.warning('halting compilation due to EMTERPRETIFY_ADVISE') + sys.exit(0) + # minify (if requested) after emterpreter processing, and finalize output logging.debug('finalizing emterpreted code') shared.Settings.FINALIZE_ASM_JS = 1 diff --git a/src/settings.js b/src/settings.js index 967d1afdc2847..f70b21edfe809 100644 --- a/src/settings.js +++ b/src/settings.js @@ -602,6 +602,10 @@ var EMTERPRETIFY_BLACKLIST = []; // Functions to not emterpret, that is, to run var EMTERPRETIFY_WHITELIST = []; // If this contains any functions, then only the functions in this list // are emterpreted (as if all the rest are blacklisted; this overrides the BLACKLIST) var EMTERPRETIFY_ASYNC = 0; // Allows sync code in the emterpreter, by saving the call stack, doing an async delay, and resuming it +var EMTERPRETIFY_ADVISE = 0; // Performs a static analysis to suggest which functions should be run in the emterpreter, as it + // appears they can be on the stack when a sync function is called in the EMTERPRETIFY_ASYNC option. + // After showing the suggested list, compilation will halt. You can apply the provided list as an + // emcc argument when compiling later. var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler var RUNNING_FASTCOMP = 1; // whether we are running the fastcomp backend diff --git a/tests/emterpreter_advise.cpp b/tests/emterpreter_advise.cpp new file mode 100644 index 0000000000000..d1a813e14ee9c --- /dev/null +++ b/tests/emterpreter_advise.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +void sleeper() { + emscripten_sleep(100); +} + +void sibling() { + printf("hi\n"); +} + +void middle(); + +void recurser() { + middle(); +} + +void middle() { + sleeper(); + sibling(); + recurser(); +} + +void pre() { + printf("bi\n"); +} + +void post() { + printf("tri\n"); +} + +int main() { + post(); + middle(); + pre(); +} + diff --git a/tests/test_other.py b/tests/test_other.py index d85e354c971dd..7b99bf5047351 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4581,6 +4581,10 @@ def test_emterpreter_swap_orig(self): out = run_js('second.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE, full_output=True, assert_returncode=None) self.validate_asmjs(out) + def test_emterpreter_advise(self): + out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'emterpreter_advise.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'EMTERPRETIFY_ADVISE=1'], stdout=PIPE).communicate() + self.assertContained('-s EMTERPRETIFY_WHITELIST=\'["__Z6middlev", "__Z7sleeperv", "__Z8recurserv", "_main"]\'', out) + def test_link_with_a_static(self): for args in [[], ['-O2']]: print args diff --git a/tools/emterpretify.py b/tools/emterpretify.py index ddfe7efe6c249..29b66bbbcf2ec 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -722,6 +722,7 @@ def process(code): SWAPPABLE = int(sys.argv[6]) if ADVISE: + # Advise the user on which functions should likely be emterpreted temp = temp_files.get('.js').name shared.Building.js_optimizer(infile, ['dumpCallGraph'], output_filename=temp, just_concat=True) asm = asm_module.AsmModule(temp) @@ -734,14 +735,17 @@ def process(code): func = curr[0] targets = curr[2] can_call[func] = set(targets) - print 'can call', can_call + # TODO function tables too - treat a function all as a function that can call anything in it, which is effectively what it is + # Note: We ignore calls in from outside the asm module, so you could do emterpreted => outside => emterpreted, and we would + # miss the first one there. But this is acceptable to do, because we can't save such a stack anyhow, due to the outside! + #print 'can call', can_call, '\n!!!\n', asm.tables, '!' reachable_from = {} for func, targets in can_call.iteritems(): for target in targets: if target not in reachable_from: reachable_from[target] = set() reachable_from[target].add(func) - print 'reachable from', reachable_from + #print 'reachable from', reachable_from # find all functions that can reach the sync funcs, which are those that can be on the stack during an async save/load, and hence must all be emterpreted to_check = list(SYNC_FUNCS) advised = set() @@ -752,7 +756,8 @@ def process(code): if reacher not in advised: advised.add(str(reacher)) to_check.append(reacher) - print "-s EMTERPRETIFY_ASYNC_WHITELIST='" + str(sorted(list(advised))).replace("'", '"') + "'" + print "Suggested list of functions to run in the emterpreter:" + print " -s EMTERPRETIFY_WHITELIST='" + str(sorted(list(advised))).replace("'", '"') + "'" sys.exit(0) BLACKLIST = set(list(BLACKLIST) + extra_blacklist) From 651c7987c3067a56c3468d1f01ae21baa96fd3c6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 16 Feb 2015 10:57:56 -0800 Subject: [PATCH 63/76] handle function tables in EMTERPRETIFY_ADVISE --- tests/emterpreter_advise_funcptr.cpp | 62 ++++++++++++++++++++++++++++ tests/test_other.py | 3 ++ tools/emterpretify.py | 18 +++++--- 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 tests/emterpreter_advise_funcptr.cpp diff --git a/tests/emterpreter_advise_funcptr.cpp b/tests/emterpreter_advise_funcptr.cpp new file mode 100644 index 0000000000000..2089fee2401a7 --- /dev/null +++ b/tests/emterpreter_advise_funcptr.cpp @@ -0,0 +1,62 @@ +#include +#include +#include + +typedef void (*func_v)(); +typedef void (*func_f)(float); +typedef void (*func_i)(int); +typedef void (*func_ii)(int, int); + +void sleeper() { + emscripten_sleep(100); +} + +void sibling(int x, int y) { + printf("hi %d, %d\n", x, y); +} + +void middle(); + +void recurser() { + middle(); +} + +void middle() { + sleeper(); + sibling(1, 2); + recurser(); +} + +void pre(float f) { + printf("bi, %f\n", f); + sibling(3, 4); + volatile func_ii ii = sibling; + ii(5, 6); +} + +void post(int y) { + printf("tri %d\n", y); + volatile func_i i = post; + i(4); + volatile func_v v = recurser; + v(); +} + +void post2(int y) { + printf("trip %d\n", y); + volatile func_i i = post; + i(4); + recurser(); +} + + +int main() { + volatile func_f f = pre; + f(1.5); + middle(); + volatile func_i i = post; + i(3); + volatile func_i i2 = post2; + i2(3); +} + diff --git a/tests/test_other.py b/tests/test_other.py index 7b99bf5047351..e8407a8381c4e 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4585,6 +4585,9 @@ def test_emterpreter_advise(self): out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'emterpreter_advise.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'EMTERPRETIFY_ADVISE=1'], stdout=PIPE).communicate() self.assertContained('-s EMTERPRETIFY_WHITELIST=\'["__Z6middlev", "__Z7sleeperv", "__Z8recurserv", "_main"]\'', out) + out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'emterpreter_advise_funcptr.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'EMTERPRETIFY_ADVISE=1'], stdout=PIPE).communicate() + self.assertContained('-s EMTERPRETIFY_WHITELIST=\'["__Z4posti", "__Z5post2i", "__Z6middlev", "__Z7sleeperv", "__Z8recurserv", "_main"]\'', out) + def test_link_with_a_static(self): for args in [[], ['-O2']]: print args diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 29b66bbbcf2ec..8a741e1fbd2fb 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -512,11 +512,16 @@ def handle_async_post_call(): CASES[ROPCODES['TSLOW']] = get_access('inst >>> 16') + ' = ' + get_coerced_access('lx') + ';' CASES[ROPCODES['TSLOWD']] = get_access('inst >>> 16', s='d') + ' = ' + get_coerced_access('lx', s='d') + ';' - opcode_used = {} for opcode in OPCODES: opcode_used[opcode] = False +def is_function_table(name): + return name.startswith('FUNCTION_TABLE_') + +def is_dyn_call(func): + return func.startswith('dynCall_') + def make_emterpreter(zero=False): # return is specialized per interpreter CASES[ROPCODES['RET']] = pop_stacktop(zero) @@ -527,7 +532,7 @@ def make_target_call(i): name = global_func_names[i] sig = global_func_sigs[i] - function_pointer_call = name.startswith('FUNCTION_TABLE_') + function_pointer_call = is_function_table(name) # our local registers are never true floats, and we just do fround calls to ensure correctness, not caring # about performance. but when coercing to outside of the emterpreter, we need to know the true sig, @@ -735,7 +740,10 @@ def process(code): func = curr[0] targets = curr[2] can_call[func] = set(targets) - # TODO function tables too - treat a function all as a function that can call anything in it, which is effectively what it is + # function tables too - treat a function all as a function that can call anything in it, which is effectively what it is + for name, funcs in asm.tables.iteritems(): + can_call[name] = set(funcs[1:-1].split(',')) + #print can_call # Note: We ignore calls in from outside the asm module, so you could do emterpreted => outside => emterpreted, and we would # miss the first one there. But this is acceptable to do, because we can't save such a stack anyhow, due to the outside! #print 'can call', can_call, '\n!!!\n', asm.tables, '!' @@ -754,7 +762,7 @@ def process(code): if curr in reachable_from: for reacher in reachable_from[curr]: if reacher not in advised: - advised.add(str(reacher)) + if not is_dyn_call(reacher) and not is_function_table(reacher): advised.add(str(reacher)) to_check.append(reacher) print "Suggested list of functions to run in the emterpreter:" print " -s EMTERPRETIFY_WHITELIST='" + str(sorted(list(advised))).replace("'", '"') + "'" @@ -782,7 +790,7 @@ def process(code): # decide which functions will be emterpreted, and find which are externally reachable (from outside other emterpreted code; those will need trampolines) - emterpreted_funcs = set([func for func in asm.funcs if func not in BLACKLIST and not func.startswith('dynCall_')]) + emterpreted_funcs = set([func for func in asm.funcs if func not in BLACKLIST and not is_dyn_call(func)]) tabled_funcs = asm.get_table_funcs() exported_funcs = [func.split(':')[0] for func in asm.exports] From 00eadfc99192c3bbf3c322e52c64309a2e89c7e5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 16 Feb 2015 19:13:45 -0800 Subject: [PATCH 64/76] mention inlining in EMTERPRETIFY_ADVISE docs --- src/settings.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/settings.js b/src/settings.js index f70b21edfe809..852a223bc2134 100644 --- a/src/settings.js +++ b/src/settings.js @@ -606,6 +606,8 @@ var EMTERPRETIFY_ADVISE = 0; // Performs a static analysis to suggest which func // appears they can be on the stack when a sync function is called in the EMTERPRETIFY_ASYNC option. // After showing the suggested list, compilation will halt. You can apply the provided list as an // emcc argument when compiling later. + // Note that this depends on things like inlining. If you run this with different inlining than + // when you use the list, it might not work. var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler var RUNNING_FASTCOMP = 1; // whether we are running the fastcomp backend From 3e41721f06c8d504082d7c1da23fe1e4d9583b69 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 16 Feb 2015 19:14:12 -0800 Subject: [PATCH 65/76] print percentage of functions in whitelist emitted from EMTERPRETIFY_ADVISE --- tools/emterpretify.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 8a741e1fbd2fb..898c491d11e0c 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -766,6 +766,7 @@ def process(code): to_check.append(reacher) print "Suggested list of functions to run in the emterpreter:" print " -s EMTERPRETIFY_WHITELIST='" + str(sorted(list(advised))).replace("'", '"') + "'" + print "(%d%% out of %d functions)" % (int((100.0*len(advised))/len(can_call)), len(can_call)) sys.exit(0) BLACKLIST = set(list(BLACKLIST) + extra_blacklist) From ed48c6c5147ee756a47816e550cd1bff0d452e7e Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Sun, 15 Feb 2015 13:14:53 -0500 Subject: [PATCH 66/76] Do not warn about missing m library. According to Issue #2600, the math library, linked with -lm, is implemented in the musl libc that is already used. Do not emit: WARNING root: emcc: cannot find library "m" --- AUTHORS | 2 +- emcc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index d04223253b80c..27cd927403432 100644 --- a/AUTHORS +++ b/AUTHORS @@ -177,4 +177,4 @@ a license to everyone to use it as detailed in LICENSE.) * Rene Eichhorn * Nick Desaulniers (copyright owned by Mozilla Foundation) * Luke Wagner (copyright owned by Mozilla Foundation) - +* Matt McCormick diff --git a/emcc b/emcc index 19d17f24c23ff..ee51682578d10 100755 --- a/emcc +++ b/emcc @@ -843,7 +843,7 @@ try: break if found: break if found: break - if not found and lib not in ['GL', 'GLU', 'glut', 'SDL']: # whitelist our default libraries + if not found and lib not in ['GL', 'GLU', 'glut', 'm', 'SDL']: # whitelist our default libraries logging.warning('emcc: cannot find library "%s"', lib) # If not compiling to JS, then we are compiling to an intermediate bitcode objects or library, so From ff534e7b6d8ddd8ce4aafe25cc3532da52c5afd7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Feb 2015 11:09:23 -0800 Subject: [PATCH 67/76] test for backend warning on lots of vars --- tests/test_other.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_other.py b/tests/test_other.py index e8407a8381c4e..b837c0a68cfab 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4802,3 +4802,16 @@ def test_sdl2_config(self): out, err = Popen([PYTHON, path_from_root('emmake'), 'sdl2-config'] + args, stdout=PIPE, stderr=PIPE).communicate() assert expected in out, out + def test_warn_toomany_vars(self): + for source, warn in [ + (path_from_root('tests', 'hello_world.c'), False), + (path_from_root('tests', 'hello_libcxx.cpp'), False), + (path_from_root('tests', 'printf', 'test.c'), True) + ]: + for opts in [0, 1, 2, 3, 's', 'z']: + print source, opts + self.clear() + out, err = Popen([PYTHON, EMCC, source, '-O' + str(opts)], stderr=PIPE).communicate() + assert os.path.exists('a.out.js') + assert ('emitted code will contain very large numbers of local variables' in err) == (warn and (opts in [0, 1])) + From fc05dbb6eb07a5fd44f38b46a3ec8801e43c1aa9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Feb 2015 13:26:08 -0800 Subject: [PATCH 68/76] save emcc args to fuzz testcases --- tests/fuzz/csmith_driver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py index cf7a21e111466..fdfeedee77388 100755 --- a/tests/fuzz/csmith_driver.py +++ b/tests/fuzz/csmith_driver.py @@ -114,6 +114,7 @@ def try_js(args): if random.random() < 0.5: js_args += ['-s', 'EMTERPRETIFY_ASYNC=1'] print '(compile)', ' '.join(js_args) + open(fullname, 'a').write('\n// ' + ' '.join(js_args) + '\n\n') shared.check_execute(js_args) assert os.path.exists(filename + '.js') print '(run in %s)' % engine1 From cdcd45c7285d699be6c5fd526227ddb18afd49d8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Feb 2015 14:03:45 -0800 Subject: [PATCH 69/76] remove other.test_emterpreter_swap_orig, which has been superceded by browser.test_asm_swapping --- tests/test_other.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/test_other.py b/tests/test_other.py index b837c0a68cfab..8b9dd7e01096e 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4574,13 +4574,6 @@ def do_log_test(source, expected, func): if old_native: os.environ['EMCC_NATIVE_OPTIMIZER'] = old_native else: del os.environ['EMCC_NATIVE_OPTIMIZER'] - def test_emterpreter_swap_orig(self): - Popen([PYTHON, EMCC, path_from_root('tests', 'fasta.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'SWAPPABLE_ASM_MODULE=1', '-O2']).communicate() - Popen([PYTHON, path_from_root('tools', 'distill_asm.py'), 'a.out.js.orig.js', 'second.js', 'swap-in']).communicate() - assert os.path.exists('second.js') - out = run_js('second.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE, full_output=True, assert_returncode=None) - self.validate_asmjs(out) - def test_emterpreter_advise(self): out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'emterpreter_advise.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'EMTERPRETIFY_ADVISE=1'], stdout=PIPE).communicate() self.assertContained('-s EMTERPRETIFY_WHITELIST=\'["__Z6middlev", "__Z7sleeperv", "__Z8recurserv", "_main"]\'', out) From 9f4af9a2def7086244dccc7ee963b4f4a8f27b82 Mon Sep 17 00:00:00 2001 From: Boris Gjenero Date: Sat, 14 Feb 2015 19:32:17 -0500 Subject: [PATCH 70/76] Add overridable function for setting title, for use by SDL If Module['setWindowTitle'] was set before, that function will be used instead. --- src/shell.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shell.js b/src/shell.js index a8dc6420639c0..58e7ccb358e25 100644 --- a/src/shell.js +++ b/src/shell.js @@ -150,6 +150,10 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { Module['load'] = importScripts; } + + if (typeof Module['setWindowTitle'] === 'undefined') { + Module['setWindowTitle'] = function(title) { document.title = title }; + } } else { // Unreachable because SHELL is dependant on the others From b77b4799022c4655dd28deb86003a26c7159d599 Mon Sep 17 00:00:00 2001 From: Boris Gjenero Date: Sat, 14 Feb 2015 23:38:38 -0500 Subject: [PATCH 71/76] SDL_WM_SetCaption() title changing via Module['setWindowTitle']() --- src/library_sdl.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/library_sdl.js b/src/library_sdl.js index 0df22ddabc1a9..bcdbbf3722026 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -1697,7 +1697,9 @@ var LibrarySDL = { #endif SDL_WM_SetCaption: function(title, icon) { - title = title && Pointer_stringify(title); + if (title && typeof Module['setWindowTitle'] !== 'undefined') { + Module['setWindowTitle'](Pointer_stringify(title)); + } icon = icon && Pointer_stringify(icon); }, From 02fa587b5d2ec6d71467320a79ca3c41fef6c17e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Feb 2015 18:07:30 -0800 Subject: [PATCH 72/76] Module.onExit --- src/postamble.js | 2 ++ tests/test_other.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/postamble.js b/src/postamble.js index c389bc9756957..030189d45e179 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -201,6 +201,8 @@ function exit(status) { // exit the runtime exitRuntime(); + if (Module['onExit']) Module['onExit'](status); + #if NODE_STDOUT_FLUSH_WORKAROUND if (ENVIRONMENT_IS_NODE) { // Work around a node.js bug where stdout buffer is not flushed at process exit: diff --git a/tests/test_other.py b/tests/test_other.py index 8b9dd7e01096e..8ac57ba5fbd6c 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4808,3 +4808,17 @@ def test_warn_toomany_vars(self): assert os.path.exists('a.out.js') assert ('emitted code will contain very large numbers of local variables' in err) == (warn and (opts in [0, 1])) + def test_module_onexit(self): + open('src.cpp', 'w').write(r''' +#include +int main() { + EM_ASM({ + Module.onExit = function(status) { Module.print('exiting now, status ' + status) }; + }); + return 14; +} + ''') + try_delete('a.out.js') + Popen([PYTHON, EMCC, 'src.cpp']).communicate() + self.assertContained('exiting now, status 14', run_js('a.out.js', assert_returncode=14)) + From da3bcb4a26abed462f59b8c24ebb47bae9b32bb5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 18 Feb 2015 11:12:02 -0800 Subject: [PATCH 73/76] check asm validation on simd tests --- tests/test_core.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index e7e6207c50cf6..fd4e32f9280f4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5285,7 +5285,6 @@ def test_cubescript(self): # Tests the full SSE1 API. def test_sse1(self): return self.skip('TODO: This test fails due to bugs #2840, #3044, #3045, #3046 and #3048 (also see #3043 and #3049)') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround orig_args = self.emcc_args @@ -5297,7 +5296,6 @@ def test_simd(self): if self.is_emterpreter(): return self.skip('todo') if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround test_path = path_from_root('tests', 'core', 'test_simd') @@ -5308,7 +5306,6 @@ def test_simd(self): def test_simd2(self): if self.is_emterpreter(): return self.skip('todo') if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround test_path = path_from_root('tests', 'core', 'test_simd2') @@ -5320,7 +5317,6 @@ def test_simd3(self): return self.skip('FIXME: this appears to be broken') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround test_path = path_from_root('tests', 'core', 'test_simd3') @@ -5332,7 +5328,6 @@ def test_simd4(self): # test_simd4 is to test phi node handling of SIMD path if self.is_emterpreter(): return self.skip('todo') if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround test_path = path_from_root('tests', 'core', 'test_simd4') @@ -5343,7 +5338,6 @@ def test_simd4(self): def test_simd5(self): # test_simd5 is to test shufflevector of SIMD path if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround test_path = path_from_root('tests', 'core', 'test_simd5') @@ -5354,7 +5348,6 @@ def test_simd5(self): def test_simd6(self): # test_simd6 is to test x86 min and max intrinsics on NaN and -0.0 return self.skip('temporarily disabled due to SpiderMonkey bug 1084609') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') if self.is_emterpreter(): return self.skip('todo') @@ -5368,7 +5361,6 @@ def test_simd7(self): # test_simd7 is to test negative zero handling. return self.skip('see issue #3103') - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') if self.is_emterpreter(): return self.skip('todo') @@ -5380,7 +5372,6 @@ def test_simd7(self): def test_simd8(self): # test_simd8 is to test unaligned load and store Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') if self.is_emterpreter(): return self.skip('todo') @@ -5390,7 +5381,6 @@ def test_simd8(self): self.do_run_from_file(src, output) def test_simd_dyncall(self): - if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') if self.is_emterpreter(): return self.skip('todo') From 41160fa339331f844590d2c739c3ac901cb06aed Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 18 Feb 2015 12:10:27 -0800 Subject: [PATCH 74/76] ports tweaks, and update sdl2 version to 5 --- tools/ports/sdl.py | 2 +- tools/system_libs.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/ports/sdl.py b/tools/ports/sdl.py index f4c4a717b86ad..4595d4e64a76c 100644 --- a/tools/ports/sdl.py +++ b/tools/ports/sdl.py @@ -1,6 +1,6 @@ import os, shutil, logging -TAG = 'version_4' +TAG = 'version_5' def get_with_configure(ports, settings, shared): # not currently used; no real need for configure on emscripten users' machines! if settings.USE_SDL == 2: diff --git a/tools/system_libs.py b/tools/system_libs.py index 6c8dbad1ef9ee..e3c9b9372b403 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -796,9 +796,9 @@ def retrieve(): # for testing. This env var should be in format # name=dir|subdir,name=dir|subdir # e.g. - # sdl2=/home/username/dev/ports/SDL2|SDL2-master + # sdl2=/home/username/dev/ports/SDL2|SDL2-version_5 # so you could run - # EMCC_LOCAL_PORTS="sdl2=/home/username/dev/ports/SDL2|SDL2-master" ./tests/runner.py browser.test_sdl2_mouse + # EMCC_LOCAL_PORTS="sdl2=/home/alon/Dev/ports/SDL2|SDL2-version_5" ./tests/runner.py browser.test_sdl2_mouse local_ports = os.environ.get('EMCC_LOCAL_PORTS') if local_ports: local_ports = map(lambda pair: pair.split('='), local_ports.split(',')) @@ -858,7 +858,7 @@ def unpack(): unpack() if not check_tag(): - logging.warning('local copy of port is too old, retrieving from remote server') + logging.warning('local copy of port is not correct, retrieving from remote server') shared.try_delete(fullname) shared.try_delete(fullname + '.zip') retrieve() From 8ff1ac1f55b6a359fb589baebb1b40bc3a246d6e Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 14 Feb 2015 14:05:18 -0800 Subject: [PATCH 75/76] Reenable test_simd6 and test_simd7. test_simd3 remains disabled due to NaN canonicalization issues. --- tests/core/test_simd3.in | 1 + tests/core/test_simd6.in | 16 ++++++++-------- tests/test_core.py | 3 --- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/core/test_simd3.in b/tests/core/test_simd3.in index f005d847e7425..f64cd237e5677 100644 --- a/tests/core/test_simd3.in +++ b/tests/core/test_simd3.in @@ -2,6 +2,7 @@ #include #include #include +#include #include using namespace std; diff --git a/tests/core/test_simd6.in b/tests/core/test_simd6.in index 0b622e0586f90..f59ad81fb6609 100644 --- a/tests/core/test_simd6.in +++ b/tests/core/test_simd6.in @@ -10,14 +10,14 @@ volatile __m128 negzero; volatile __m128 zero; -volatile __m128 nan; +volatile __m128 nan_; volatile __m128 one; int main() { negzero = _mm_set1_ps(-0.0); zero = _mm_set1_ps(0.0); one = _mm_set1_ps(1.0); - nan = _mm_set1_ps(__builtin_nan("")); + nan_ = _mm_set1_ps(__builtin_nan("")); __m128 expected, result; @@ -29,12 +29,12 @@ int main() { expected = negzero; assert(memcmp(&result, &expected, sizeof(__m128)) == 0); - result = _mm_max_ps(nan, zero); + result = _mm_max_ps(nan_, zero); expected = zero; assert(memcmp(&result, &expected, sizeof(__m128)) == 0); - result = _mm_max_ps(zero, nan); - expected = nan; + result = _mm_max_ps(zero, nan_); + expected = nan_; assert(memcmp(&result, &expected, sizeof(__m128)) == 0); result = _mm_max_ps(zero, one); @@ -49,12 +49,12 @@ int main() { expected = negzero; assert(memcmp(&result, &expected, sizeof(__m128)) == 0); - result = _mm_min_ps(nan, zero); + result = _mm_min_ps(nan_, zero); expected = zero; assert(memcmp(&result, &expected, sizeof(__m128)) == 0); - result = _mm_min_ps(zero, nan); - expected = nan; + result = _mm_min_ps(zero, nan_); + expected = nan_; assert(memcmp(&result, &expected, sizeof(__m128)) == 0); result = _mm_min_ps(zero, one); diff --git a/tests/test_core.py b/tests/test_core.py index fd4e32f9280f4..8e5c0c3fecfaa 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5347,7 +5347,6 @@ def test_simd5(self): def test_simd6(self): # test_simd6 is to test x86 min and max intrinsics on NaN and -0.0 - return self.skip('temporarily disabled due to SpiderMonkey bug 1084609') Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') if self.is_emterpreter(): return self.skip('todo') @@ -5359,8 +5358,6 @@ def test_simd6(self): def test_simd7(self): # test_simd7 is to test negative zero handling. - return self.skip('see issue #3103') - if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') if self.is_emterpreter(): return self.skip('todo') From 36d3bc8dde77cb615e2d42ebecebdf0b0036d8ca Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 18 Feb 2015 15:52:52 -0800 Subject: [PATCH 76/76] update simd coercion handling in optimizer to latest simd spec; 1.29.10 --- emscripten-version.txt | 2 +- tests/optimizer/simd-output.js | 7 +++++++ tests/optimizer/simd.js | 8 ++++++++ tests/test_other.py | 2 ++ tools/js-optimizer.js | 14 ++++++++------ tools/optimizer/optimizer.cpp | 11 +++++++---- 6 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 tests/optimizer/simd-output.js create mode 100644 tests/optimizer/simd.js diff --git a/emscripten-version.txt b/emscripten-version.txt index 7205e73599a35..60ddee3e59ca3 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.29.9 +1.29.10 diff --git a/tests/optimizer/simd-output.js b/tests/optimizer/simd-output.js new file mode 100644 index 0000000000000..29b6d8dea7db3 --- /dev/null +++ b/tests/optimizer/simd-output.js @@ -0,0 +1,7 @@ +function a(x, y) { + x = SIMD_int32x4_check(x); + y = SIMD_float32x4_check(y); + var z = SIMD_float32x4(0, 0, 0, 0); + work(z); +} + diff --git a/tests/optimizer/simd.js b/tests/optimizer/simd.js new file mode 100644 index 0000000000000..0c715a8983d26 --- /dev/null +++ b/tests/optimizer/simd.js @@ -0,0 +1,8 @@ +function a(x, y) { + x = SIMD_int32x4_check(x); + y = SIMD_float32x4_check(y); + var z = SIMD_float32x4(0, 0, 0, 0); + work(z); +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a"] + diff --git a/tests/test_other.py b/tests/test_other.py index 8ac57ba5fbd6c..213b98334e2a3 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1984,6 +1984,8 @@ def test_js_optimizer(self): ['asm', 'ensureLabelSet']), (path_from_root('tests', 'optimizer', '3154.js'), open(path_from_root('tests', 'optimizer', '3154-output.js')).read(), ['asm', 'eliminate', 'registerize', 'asmLastOpts', 'last']), + (path_from_root('tests', 'optimizer', 'simd.js'), open(path_from_root('tests', 'optimizer', 'simd-output.js')).read(), + ['asm', 'eliminate']), # eliminate, just enough to trigger asm normalization/denormalization ]: print input, passes diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 89167ebde7bad..dc93e6f9613a0 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2148,9 +2148,11 @@ function detectType(node, asmInfo, inVarDef) { case 'call': { if (node[1][0] === 'name') { switch (node[1][1]) { - case 'Math_fround': return ASM_FLOAT; - case 'SIMD_float32x4': return ASM_FLOAT32X4; - case 'SIMD_int32x4': return ASM_INT32X4; + case 'Math_fround': return ASM_FLOAT; + case 'SIMD_float32x4': + case 'SIMD_float32x4_check': return ASM_FLOAT32X4; + case 'SIMD_int32x4': + case 'SIMD_int32x4_check': return ASM_INT32X4; default: break; } } @@ -2208,8 +2210,8 @@ function makeAsmCoercion(node, type) { case ASM_INT: return ['binary', '|', node, ['num', 0]]; case ASM_DOUBLE: return ['unary-prefix', '+', node]; case ASM_FLOAT: return ['call', ['name', 'Math_fround'], [node]]; - case ASM_FLOAT32X4: return ['call', ['name', 'SIMD_float32x4'], [node]]; - case ASM_INT32X4: return ['call', ['name', 'SIMD_int32x4'], [node]]; + case ASM_FLOAT32X4: return ['call', ['name', 'SIMD_float32x4_check'], [node]]; + case ASM_INT32X4: return ['call', ['name', 'SIMD_int32x4_check'], [node]]; case ASM_NONE: default: return node; // non-validating code, emit nothing XXX this is dangerous, we should only allow this when we know we are not validating } @@ -2238,7 +2240,7 @@ function makeAsmVarDef(v, type) { case ASM_INT32X4: { return [v, ['call', ['name', 'SIMD_int32x4'], [['num', 0], ['num', 0], ['num', 0], ['num', 0]]]]; } - default: throw 'wha? ' + JSON.stringify([node, type]) + new Error().stack; + default: throw 'wha? ' + JSON.stringify([v, type]) + new Error().stack; } } diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp index 586fa8ec21bc9..f87ec3bc90d68 100644 --- a/tools/optimizer/optimizer.cpp +++ b/tools/optimizer/optimizer.cpp @@ -14,6 +14,9 @@ typedef std::vector StringVec; Ref doc, extraInfo; +IString SIMD_INT32X4_CHECK("SIMD_int32x4_check"), + SIMD_FLOAT32X4_CHECK("SIMD_float32x4_check"); + //================== // Infrastructure //================== @@ -352,8 +355,8 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { if (node[1][0] == NAME) { IString name = node[1][1]->getIString(); if (name == MATH_FROUND) return ASM_FLOAT; - else if (name == SIMD_FLOAT32X4) return ASM_FLOAT32X4; - else if (name == SIMD_INT32X4) return ASM_INT32X4; + else if (name == SIMD_FLOAT32X4 || name == SIMD_FLOAT32X4_CHECK) return ASM_FLOAT32X4; + else if (name == SIMD_INT32X4 || name == SIMD_INT32X4_CHECK) return ASM_INT32X4; } return ASM_NONE; } else if (node[0] == CONDITIONAL) { @@ -508,8 +511,8 @@ Ref makeAsmCoercion(Ref node, AsmType type) { case ASM_INT: return make3(BINARY, OR, node, makeNum(0)); case ASM_DOUBLE: return make2(UNARY_PREFIX, PLUS, node); case ASM_FLOAT: return make2(CALL, makeName(MATH_FROUND), &(makeArray())->push_back(node)); - case ASM_FLOAT32X4: return make2(CALL, makeName(SIMD_FLOAT32X4), &(makeArray())->push_back(node)); - case ASM_INT32X4: return make2(CALL, makeName(SIMD_INT32X4), &(makeArray())->push_back(node)); + case ASM_FLOAT32X4: return make2(CALL, makeName(SIMD_FLOAT32X4_CHECK), &(makeArray())->push_back(node)); + case ASM_INT32X4: return make2(CALL, makeName(SIMD_INT32X4_CHECK), &(makeArray())->push_back(node)); case ASM_NONE: default: return node; // non-validating code, emit nothing XXX this is dangerous, we should only allow this when we know we are not validating }