diff --git a/AUTHORS b/AUTHORS index b613403b8f02c..27cd927403432 100644 --- a/AUTHORS +++ b/AUTHORS @@ -176,3 +176,5 @@ a license to everyone to use it as detailed in LICENSE.) * Edward Rudd * 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 2501c4a72577d..ee51682578d10 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 = int(os.environ.get('EMCONFIGURE_JS') or 1) if debug_configure: tempout = '/tmp/emscripten_temp/out' @@ -257,7 +259,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] @@ -384,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 @@ -837,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 @@ -1046,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 @@ -1426,7 +1433,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') @@ -1586,13 +1595,23 @@ 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: + args += ['ASSERTIONS=1'] + if shared.Settings.PRECISE_F32: + args += ['FROUND=1'] execute(args) final = final + '.em.js' 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 @@ -1810,4 +1829,5 @@ finally: pass else: logging.info('emcc saved files are in:' + temp_dir) + misc_temp_files.clean() 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/emscripten.py b/emscripten.py index 800045e423798..1c4a4ba687735 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1093,13 +1093,13 @@ 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', '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/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index 9d4453759147c..15ca9934e46bf 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -1073,6 +1073,81 @@ 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. + +Sleeping +-------- + +.. 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. + +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 ================== @@ -1084,12 +1159,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 +1182,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/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/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. 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); diff --git a/src/library_async.js b/src/library_async.js index fe065bc61ddd5..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; }, @@ -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') }}}; }, @@ -202,15 +202,31 @@ 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 // 1 - saving // 2 - loading + saveStack: '', + 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(); 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 @@ -221,11 +237,30 @@ 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); + // 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 + 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(); + } }); EmterpreterAsync.setState(1); +#if ASSERTIONS + this.saveStack = stackTrace(); +#endif + // Pause the main loop, until we resume + if (Browser.mainLoop.func) { + Browser.mainLoop.pause(); + } + if (!yieldDuring) Browser.pauseAsyncCallbacks(); } else { // nothing to do here, the stack was just recreated. reset the state. assert(EmterpreterAsync.state === 2); @@ -237,10 +272,20 @@ 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); }); }, + 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/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/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); }, 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/src/postamble.js b/src/postamble.js index 5ba8dd4ff9a70..030189d45e179 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; + }); + } } } @@ -166,7 +189,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; } @@ -178,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: @@ -203,10 +228,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 +248,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/src/settings.js b/src/settings.js index 967d1afdc2847..852a223bc2134 100644 --- a/src/settings.js +++ b/src/settings.js @@ -602,6 +602,12 @@ 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. + // 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 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 diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 98a330d7d704a..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 @@ -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/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) diff --git a/system/include/emscripten/xmmintrin.h b/system/include/emscripten/xmmintrin.h index 7ffce688ca4a5..2958e5b581a20 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 + return __builtin_shufflevector(emscripten_float32x4_loadxy(__p), __a, 0, 1, 6, 7); } 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__)) 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/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/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/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/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/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/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/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py index a0f1ffb312355..fdfeedee77388 100755 --- a/tests/fuzz/csmith_driver.py +++ b/tests/fuzz/csmith_driver.py @@ -102,7 +102,19 @@ 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: + 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) + 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 @@ -159,5 +171,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 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/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/runner.py b/tests/runner.py index 77d06ef6bbb82..d3f2e8b233615 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 = [] @@ -723,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() diff --git a/tests/test_browser.py b/tests/test_browser.py index 7156d0e4c3cd6..cfc1cbcfcf6cf 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; @@ -2052,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: @@ -2467,19 +2494,37 @@ 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']) 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 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"]']) + def test_emterpreter_async_bad(self): + 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']) + + 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_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']]: diff --git a/tests/test_core.py b/tests/test_core.py index 6bc525a39c6d4..8e5c0c3fecfaa 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')) @@ -1865,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')) @@ -2253,14 +2256,12 @@ 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')) 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 @@ -2849,7 +2850,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') @@ -5124,7 +5125,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: @@ -5277,10 +5277,14 @@ 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)') - 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 @@ -5292,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') @@ -5303,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') @@ -5315,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') @@ -5327,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') @@ -5338,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') @@ -5348,8 +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') - 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') @@ -5361,9 +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 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') @@ -5375,7 +5369,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') @@ -5385,7 +5378,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') @@ -5409,6 +5401,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 @@ -5461,7 +5456,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(), @@ -6059,7 +6053,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) @@ -6961,7 +6955,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] @@ -7217,10 +7213,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 @@ -7232,7 +7228,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 @@ -7322,9 +7317,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'); } } }); diff --git a/tests/test_other.py b/tests/test_other.py index 95bc1a773194b..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 @@ -4390,7 +4392,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' @@ -4532,6 +4534,19 @@ 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') + + do_js_test('flexible mod', r''' +function _main() { + print(1 % 16); +} +''', [], '1\n') + # codegen log tests def do_log_test(source, expected, func): @@ -4561,12 +4576,12 @@ 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) + + 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']]: @@ -4632,13 +4647,19 @@ 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) + 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() + 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'] def test_emcc_c_multi(self): def test(args, llvm_opts=None): @@ -4776,3 +4797,30 @@ 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])) + + 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)) + diff --git a/tools/asm_module.py b/tools/asm_module.py index fcf86ae5509d6..cb6468327fe82 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 @@ -306,3 +315,25 @@ 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): + 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 or is_float(imp): + return 'd' + else: + return '?' + diff --git a/tools/emterpretify.py b/tools/emterpretify.py index f1a2e85a2cf14..898c491d11e0c 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -22,15 +22,19 @@ ASSERTIONS = False PROFILING = False SWAPPABLE = False +FROUND = False +ADVISE = False def handle_arg(arg): - global ZERO, ASYNC, ASSERTIONS, PROFILING + global ZERO, ASYNC, ASSERTIONS, PROFILING, FROUND, ADVISE 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) + elif l == 'ADVISE': ADVISE = int(r) return False return True @@ -42,6 +46,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 @@ -49,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) @@ -194,8 +203,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. @@ -218,6 +228,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 +270,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 +281,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 +435,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 @@ -494,6 +512,15 @@ 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 @@ -505,14 +532,28 @@ 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, + # and must use frounds + 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': + 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: @@ -522,8 +563,8 @@ def make_target_call(i): 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() @@ -546,22 +587,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) if global_var_types[rglobal_vars[i]] == t]) + \ + '\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) if global_var_types[rglobal_vars[i]] == t]) + \ + '\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 @@ -587,7 +637,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 @@ -676,6 +726,49 @@ def process(code): if len(sys.argv) >= 7: 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) + 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) + # 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, '!' + 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: + 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("'", '"') + "'" + 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) if DEBUG or SWAPPABLE: @@ -698,7 +791,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] @@ -721,7 +814,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) @@ -772,12 +865,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'], 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 + 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): @@ -805,20 +916,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] - assert '|0' in imp or '| 0' in imp or imp == '0' - 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']: + 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 @@ -830,7 +933,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] @@ -851,15 +954,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] @@ -882,6 +978,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 @@ -927,8 +1024,10 @@ 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) +temp_files.clean() + diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index d89484057ab28..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; } } @@ -2307,6 +2309,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)); } @@ -2333,6 +2343,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 = { @@ -6120,6 +6138,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; @@ -6130,6 +6155,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'); @@ -6157,12 +6183,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)); } } } @@ -6273,9 +6299,10 @@ 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 + // 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]]; } } @@ -6328,8 +6355,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 } } @@ -6494,7 +6521,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 '-': { @@ -6550,6 +6588,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]; } @@ -6783,7 +6823,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); @@ -6793,7 +6833,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); @@ -6853,6 +6893,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))); @@ -6993,6 +7034,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 @@ -7002,7 +7057,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'; @@ -7105,6 +7159,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 { @@ -7159,7 +7220,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; @@ -7376,11 +7437,70 @@ function emterpretify(ast) { var ignore = !(func[1] in EMTERPRETED_FUNCS); if (ignore) { + // we are not emterpreting this function + 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. + // 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 = []; + 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 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)); } var asmData = normalizeAsm(func); - print('// return type: [' + func[1] + ',' + asmData.ret + ']'); + print('// return type: [' + func[1] + ',' + getSignature(func, asmData) + ']'); if (ignore) { return; @@ -7388,8 +7508,42 @@ 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 + var numVars = 0; function countLocals() { numLocals = 0; @@ -7398,6 +7552,7 @@ function emterpretify(ast) { } for (var i in asmData.vars) { numLocals++; + numVars++; } } @@ -7416,8 +7571,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++; @@ -7479,6 +7639,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 @@ -7497,8 +7658,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)); @@ -7527,6 +7688,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'; } @@ -7556,6 +7718,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) @@ -7713,6 +7895,7 @@ var passes = { ensureLabelSet: ensureLabelSet, emterpretify: emterpretify, findReachable: findReachable, + dumpCallGraph: dumpCallGraph, asmLastOpts: asmLastOpts, noop: function() {}, 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 } 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()