diff --git a/AUTHORS b/AUTHORS index 2ff3bd9469249..d356a05b3a743 100644 --- a/AUTHORS +++ b/AUTHORS @@ -142,4 +142,6 @@ a license to everyone to use it as detailed in LICENSE.) * Ophir Lojkine * Ryan Sturgell (copyright owned by Google, Inc.) * Jason Green (copyright owned by TransGaming, Inc.) +* Ningxin Hu (copyright owned by Intel) +* Nicolas Guillemot diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake index f9d8c7737788a..362a552d1b9a2 100644 --- a/cmake/Platform/Emscripten.cmake +++ b/cmake/Platform/Emscripten.cmake @@ -98,6 +98,8 @@ set(CMAKE_SYSTEM_INCLUDE_PATH "${EMSCRIPTEN_ROOT_PATH}/system/include") #SET(CMAKE_FIND_LIBRARY_PREFIXES "") #SET(CMAKE_FIND_LIBRARY_SUFFIXES ".bc") +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES 1) +SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_LIBRARIES 1) SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1) SET(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1) @@ -107,10 +109,8 @@ set(CMAKE_C_RESPONSE_FILE_LINK_FLAG "@") set(CMAKE_CXX_RESPONSE_FILE_LINK_FLAG "@") # Specify the program to use when building static libraries. Force Emscripten-related command line options to clang. -set(CMAKE_CXX_ARCHIVE_CREATE "${CMAKE_AR} rc ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") -set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_AR} rc ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") -set(CMAKE_CXX_ARCHIVE_APPEND "${CMAKE_AR} r ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") -set(CMAKE_C_ARCHIVE_APPEND "${CMAKE_AR} r ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") +set(CMAKE_C_CREATE_STATIC_LIBRARY " rc ") +set(CMAKE_CXX_CREATE_STATIC_LIBRARY " rc ") # Set a global EMSCRIPTEN variable that can be used in client CMakeLists.txt to detect when building using Emscripten. set(EMSCRIPTEN 1 CACHE BOOL "If true, we are targeting Emscripten output.") diff --git a/emcc b/emcc index 37a17ab1c05f8..0dfa5d8634336 100755 --- a/emcc +++ b/emcc @@ -1347,6 +1347,7 @@ try: if shared.Settings.MAIN_MODULE: assert not shared.Settings.SIDE_MODULE shared.Settings.INCLUDE_FULL_LIBRARY = 1 + shared.Settings.EXPORT_ALL = 1 elif shared.Settings.SIDE_MODULE: assert not shared.Settings.MAIN_MODULE @@ -1375,6 +1376,8 @@ try: if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT: assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet' + assert not (bind and shared.Settings.NO_DYNAMIC_EXECUTION), 'NO_DYNAMIC_EXECUTION disallows embind' + if proxy_to_worker: shared.Settings.PROXY_TO_WORKER = 1 diff --git a/emscripten-version.txt b/emscripten-version.txt index de59d0c645d25..5f079d01d726a 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.19.1 +1.19.2 diff --git a/src/deps_info.json b/src/deps_info.json index 029a20e186546..e098306475d0c 100644 --- a/src/deps_info.json +++ b/src/deps_info.json @@ -3,6 +3,7 @@ "SDL_Init": ["malloc", "free"], "SDL_GL_GetProcAddress": ["emscripten_GetProcAddress"], "eglGetProcAddress": ["emscripten_GetProcAddress"], + "glfwGetProcAddress": ["emscripten_GetProcAddress"], "emscripten_GetProcAddress": ["strstr"] } diff --git a/src/jsifier.js b/src/jsifier.js index 065c66a81b5f8..791273f444fd9 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1321,10 +1321,10 @@ function JSify(data, functionsOnly) { // vector load var native = getVectorNativeType(item.valueType); var base = getSIMDName(native); - return base + '32x4(' + makeGetValue(value, 0, native, 0, item.unsigned, 0, item.align) + ',' + - makeGetValue(value, 4, native, 0, item.unsigned, 0, item.align) + ',' + - makeGetValue(value, 8, native, 0, item.unsigned, 0, item.align) + ',' + - makeGetValue(value, 12, native, 0, item.unsigned, 0, item.align) + ');'; + return 'SIMD.' + base + '32x4(' + makeGetValue(value, 0, native, 0, item.unsigned, 0, item.align) + ',' + + makeGetValue(value, 4, native, 0, item.unsigned, 0, item.align) + ',' + + makeGetValue(value, 8, native, 0, item.unsigned, 0, item.align) + ',' + + makeGetValue(value, 12, native, 0, item.unsigned, 0, item.align) + ');'; } var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED; switch (impl) { @@ -1395,7 +1395,7 @@ function JSify(data, functionsOnly) { } for (var i = 0; i < 4; i++) assert(mask[0] == 0 || mask == 1); i = 0; - return base + '32x4(' + mask.map(function(m) { + return 'SIMD.' + base + '32x4(' + mask.map(function(m) { return (m == 1 ? second : first) + '.' + simdLane[i++]; }).join(',') + ')'; } diff --git a/src/library.js b/src/library.js index 120def05448b3..4f800640e8f79 100644 --- a/src/library.js +++ b/src/library.js @@ -3166,7 +3166,7 @@ LibraryManager.library = { #endif environ: 'allocate(1, "i32*", ALLOC_STATIC)', __environ__deps: ['environ'], - __environ: '_environ', + __environ: 'environ', __buildEnvironment__deps: ['__environ'], __buildEnvironment: function(env) { // WARNING: Arbitrary limit! @@ -3353,6 +3353,7 @@ LibraryManager.library = { return 0; } else { var size = Math.min(4095, absolute.path.length); // PATH_MAX - 1. + if (resolved_name === 0) resolved_name = _malloc(size+1); for (var i = 0; i < size; i++) { {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}}; } diff --git a/src/library_browser.js b/src/library_browser.js index 4ef7c577d9180..57ca5a24625f6 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -1175,6 +1175,39 @@ mergeInto(LibraryManager.library, { var info = Browser.workers[id]; if (!info) return -1; return info.awaited; + }, + + emscripten_get_preloaded_image_data: function(path, w, h) { + if (typeof path === "number") { + path = Pointer_stringify(path); + } + + path = PATH.resolve(path); + + var canvas = Module["preloadedImages"][path]; + if (canvas) { + var ctx = canvas.getContext("2d"); + var image = ctx.getImageData(0, 0, canvas.width, canvas.height); + var buf = _malloc(canvas.width * canvas.height * 4); + + HEAPU8.set(image.data, buf); + + {{{ makeSetValue('w', '0', 'canvas.width', 'i32') }}}; + {{{ makeSetValue('h', '0', 'canvas.height', 'i32') }}}; + return buf; + } + + return 0; + }, + + emscripten_get_preloaded_image_data_from_FILE__deps: ['emscripten_get_preloaded_image_data'], + emscripten_get_preloaded_image_data_from_FILE: function(file, w, h) { + var stream = FS.getStreamFromPtr(file); + if (stream) { + return _emscripten_get_preloaded_image_data(stream.path, w, h); + } + + return 0; } }); diff --git a/src/library_glfw.js b/src/library_glfw.js index 6d53932603681..6dfea10134e36 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -417,6 +417,9 @@ var LibraryGLFW = { glfwSetWindowSizeCallback: function(cbfun) { GLFW.resizeFunc = cbfun; + if (GLFW.resizeFunc) { + Runtime.dynCall('vii', GLFW.resizeFunc, [Module['canvas'].width, Module['canvas'].height]); + } }, glfwSetWindowCloseCallback: function(cbfun) { @@ -507,9 +510,9 @@ var LibraryGLFW = { return Module.ctx.getSupportedExtensions().indexOf(Pointer_stringify(extension)) > -1; }, - glfwGetProcAddress__deps: ['glfwGetProcAddress'], + glfwGetProcAddress__deps: ['emscripten_GetProcAddress'], glfwGetProcAddress: function(procname) { - return _getProcAddress(procname); + return _emscripten_GetProcAddress(procname); }, glfwGetGLVersion: function(major, minor, rev) { diff --git a/src/library_sdl.js b/src/library_sdl.js index a01b3c6ca1163..7145a7babe455 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -2601,7 +2601,7 @@ var LibrarySDL = { if (info && info.audio) { info.audio.pause(); } else { - Module.printErr('Mix_Pause: no sound found for channel: ' + channel); + //Module.printErr('Mix_Pause: no sound found for channel: ' + channel); } }, diff --git a/src/modules.js b/src/modules.js index fd5c23cd65039..56f4c827a3e18 100644 --- a/src/modules.js +++ b/src/modules.js @@ -443,6 +443,31 @@ var LibraryManager = { } } + // apply synonyms. these are typically not speed-sensitive, and doing it this way makes it possible to not include hacks in the compiler + // (and makes it simpler to switch between SDL verisons, fastcomp and non-fastcomp, etc.). + var lib = LibraryManager.library; + libloop: for (var x in lib) { + if (x.lastIndexOf('__') > 0) continue; // ignore __deps, __* + if (lib[x + '__asm']) continue; // ignore asm library functions, those need to be fully optimized + if (typeof lib[x] === 'string') { + var target = x; + while (typeof lib[target] === 'string') { + if (lib[target].indexOf('(') >= 0) continue libloop; + target = lib[target]; + } + if (typeof lib[target] === 'undefined' || typeof lib[target] === 'function') { + if (target.indexOf('Math_') < 0) { + lib[x] = new Function('return _' + target + '.apply(null, arguments)'); + if (!lib[x + '__deps']) lib[x + '__deps'] = []; + lib[x + '__deps'].push(target); + } else { + lib[x] = new Function('return ' + target + '.apply(null, arguments)'); + } + continue; + } + } + } + /* // export code for CallHandlers.h printErr('============================'); diff --git a/src/parseTools.js b/src/parseTools.js index 0c413afac0172..ececf477683cf 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -2035,7 +2035,7 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) { } else if (param.intertype == 'mathop') { return processMathop(param); } else if (param.intertype === 'vector') { - return getVectorBaseType(param.type) + '32x4(' + param.idents.join(',') + ')'; + return 'SIMD.' + getVectorBaseType(param.type) + '32x4(' + param.idents.join(',') + ')'; } else { throw 'invalid llvm parameter: ' + param.intertype; } @@ -2700,7 +2700,7 @@ var simdLane = ['x', 'y', 'z', 'w']; function ensureVector(ident, base) { Types.usesSIMD = true; - return ident == 0 ? base + '32x4.splat(0)' : ident; + return ident == 0 ? 'SIMD.' + base + '32x4.splat(0)' : ident; } function ensureValidFFIType(type) { diff --git a/src/preamble.js b/src/preamble.js index 431a3c2747f2e..ae58e7e0990c9 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -401,7 +401,7 @@ var cwrap, ccall; return ret; } - var sourceRegex = /^function\s\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/; + var sourceRegex = /^function\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/; function parseJSFunc(jsfunc) { // Match the body and the return value of a javascript function source var parsed = jsfunc.toString().match(sourceRegex).slice(1); diff --git a/src/simd.js b/src/simd.js index 6e3e3675a5bf6..d5ff8b156b4be 100644 --- a/src/simd.js +++ b/src/simd.js @@ -22,6 +22,9 @@ "use strict"; +// SIMD module. +var SIMD = {}; + /** * Construct a new instance of float32x4 number. * @param {double} value used for x lane. @@ -30,9 +33,9 @@ * @param {double} value used for w lane. * @constructor */ -function float32x4(x, y, z, w) { - if (!(this instanceof float32x4)) { - return new float32x4(x, y, z, w); +SIMD.float32x4 = function(x, y, z, w) { + if (!(this instanceof SIMD.float32x4)) { + return new SIMD.float32x4(x, y, z, w); } this.storage_ = new Float32Array(4); this.storage_[0] = x; @@ -45,8 +48,8 @@ function float32x4(x, y, z, w) { * Construct a new instance of float32x4 number with 0.0 in all lanes. * @constructor */ -float32x4.zero = function() { - return float32x4(0.0, 0.0, 0.0, 0.0); +SIMD.float32x4.zero = function() { + return SIMD.float32x4(0.0, 0.0, 0.0, 0.0); } /** @@ -55,38 +58,10 @@ float32x4.zero = function() { * @param {double} value used for all lanes. * @constructor */ -float32x4.splat = function(s) { - return float32x4(s, s, s, s); +SIMD.float32x4.splat = function(s) { + return SIMD.float32x4(s, s, s, s); } -Object.defineProperty(float32x4.prototype, 'x', { - get: function() { return this.storage_[0]; } -}); - -Object.defineProperty(float32x4.prototype, 'y', { - get: function() { return this.storage_[1]; } -}); - -Object.defineProperty(float32x4.prototype, 'z', { - get: function() { return this.storage_[2]; } -}); - -Object.defineProperty(float32x4.prototype, 'w', - { get: function() { return this.storage_[3]; } -}); - -/** - * Extract the sign bit from each lane return them in the first 4 bits. - */ -Object.defineProperty(float32x4.prototype, 'signMask', { - get: function() { - var mx = this.x < 0.0 ? 1 : 0; - var my = this.y < 0.0 ? 1 : 0; - var mz = this.z < 0.0 ? 1 : 0; - var mw = this.w < 0.0 ? 1 : 0; - return mx | my << 1 | mz << 2 | mw << 3; - } -}); /** * Construct a new instance of int32x4 number. @@ -96,9 +71,9 @@ Object.defineProperty(float32x4.prototype, 'signMask', { * @param {integer} 32-bit unsigned value used for w lane. * @constructor */ -function int32x4(x, y, z, w) { - if (!(this instanceof int32x4)) { - return new int32x4(x, y, z, w); +SIMD.int32x4 = function(x, y, z, w) { + if (!(this instanceof SIMD.int32x4)) { + return new SIMD.int32x4(x, y, z, w); } this.storage_ = new Int32Array(4); this.storage_[0] = x; @@ -107,6 +82,14 @@ function int32x4(x, y, z, w) { this.storage_[3] = w; } +/** + * Construct a new instance of int32x4 number with 0 in all lanes. + * @constructor + */ +SIMD.int32x4.zero = function() { + return SIMD.int32x4(0, 0, 0, 0); +} + /** * Construct a new instance of int32x4 number with 0xFFFFFFFF or 0x0 in each * lane, depending on the truth value in x, y, z, and w. @@ -116,11 +99,11 @@ function int32x4(x, y, z, w) { * @param {boolean} flag used for w lane. * @constructor */ -int32x4.bool = function(x, y, z, w) { - return int32x4(x ? -1 : 0x0, - y ? -1 : 0x0, - z ? -1 : 0x0, - w ? -1 : 0x0); +SIMD.int32x4.bool = function(x, y, z, w) { + return SIMD.int32x4(x ? -1 : 0x0, + y ? -1 : 0x0, + z ? -1 : 0x0, + w ? -1 : 0x0); } /** @@ -129,746 +112,637 @@ int32x4.bool = function(x, y, z, w) { * @param {integer} value used for all lanes. * @constructor */ -int32x4.splat = function(s) { - return int32x4(s, s, s, s); +SIMD.int32x4.splat = function(s) { + return SIMD.int32x4(s, s, s, s); } -Object.defineProperty(int32x4.prototype, 'x', { - get: function() { return this.storage_[0]; } -}); +/** +* @return {float32x4} New instance of float32x4 with absolute values of +* t. +*/ +SIMD.float32x4.abs = function(t) { + return SIMD.float32x4(Math.abs(t.x), Math.abs(t.y), Math.abs(t.z), + Math.abs(t.w)); +} -Object.defineProperty(int32x4.prototype, 'y', { - get: function() { return this.storage_[1]; } -}); +/** + * @return {float32x4} New instance of float32x4 with negated values of + * t. + */ +SIMD.float32x4.neg = function(t) { + return SIMD.float32x4(-t.x, -t.y, -t.z, -t.w); +} -Object.defineProperty(int32x4.prototype, 'z', { - get: function() { return this.storage_[2]; } -}); +/** + * @return {float32x4} New instance of float32x4 with a + b. + */ +SIMD.float32x4.add = function(a, b) { + return SIMD.float32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} -Object.defineProperty(int32x4.prototype, 'w', - { get: function() { return this.storage_[3]; } -}); +/** + * @return {float32x4} New instance of float32x4 with a - b. + */ +SIMD.float32x4.sub = function(a, b) { + return SIMD.float32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} -Object.defineProperty(int32x4.prototype, 'flagX', { - get: function() { return this.storage_[0] != 0x0; } -}); +/** + * @return {float32x4} New instance of float32x4 with a * b. + */ +SIMD.float32x4.mul = function(a, b) { + return SIMD.float32x4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); +} -Object.defineProperty(int32x4.prototype, 'flagY', { - get: function() { return this.storage_[1] != 0x0; } -}); +/** + * @return {float32x4} New instance of float32x4 with a / b. + */ +SIMD.float32x4.div = function(a, b) { + return SIMD.float32x4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); +} -Object.defineProperty(int32x4.prototype, 'flagZ', { - get: function() { return this.storage_[2] != 0x0; } -}); +/** + * @return {float32x4} New instance of float32x4 with t's values clamped + * between lowerLimit and upperLimit. + */ +SIMD.float32x4.clamp = function(t, lowerLimit, 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; + var cw = t.w < lowerLimit.w ? lowerLimit.w : t.w; + cx = cx > upperLimit.x ? upperLimit.x : cx; + cy = cy > upperLimit.y ? upperLimit.y : cy; + cz = cz > upperLimit.z ? upperLimit.z : cz; + cw = cw > upperLimit.w ? upperLimit.w : cw; + return SIMD.float32x4(cx, cy, cz, cw); +} -Object.defineProperty(int32x4.prototype, 'flagW', - { get: function() { return this.storage_[3] != 0x0; } -}); +/** + * @return {float32x4} New instance of float32x4 with the minimum value of + * t and other. + */ +SIMD.float32x4.min = function(t, other) { + var cx = t.x > other.x ? other.x : t.x; + var cy = t.y > other.y ? other.y : t.y; + var cz = t.z > other.z ? other.z : t.z; + var cw = t.w > other.w ? other.w : t.w; + return SIMD.float32x4(cx, cy, cz, cw); +} /** - * Extract the sign bit from each lane return them in the first 4 bits. + * @return {float32x4} New instance of float32x4 with the maximum value of + * t and other. */ -Object.defineProperty(int32x4.prototype, 'signMask', { - get: function() { - var mx = (this.storage_[0] & 0x80000000) >>> 31; - var my = (this.storage_[1] & 0x80000000) >>> 31; - var mz = (this.storage_[2] & 0x80000000) >>> 31; - var mw = (this.storage_[3] & 0x80000000) >>> 31; - return mx | my << 1 | mz << 2 | mw << 3; - } -}); +SIMD.float32x4.max = function(t, other) { + var cx = t.x < other.x ? other.x : t.x; + var cy = t.y < other.y ? other.y : t.y; + var cz = t.z < other.z ? other.z : t.z; + var cw = t.w < other.w ? other.w : t.w; + return SIMD.float32x4(cx, cy, cz, cw); +} -function isNumber(o) { - return typeof o == "number" || (typeof o == "object" && o.constructor === Number); +/** + * @return {float32x4} New instance of float32x4 with reciprocal value of + * t. + */ +SIMD.float32x4.reciprocal = function(t) { + return SIMD.float32x4(1.0 / t.x, 1.0 / t.y, 1.0 / t.z, 1.0 / t.w); } -function isTypedArray(o) { - return (o instanceof Int8Array) || - (o instanceof Uint8Array) || - (o instanceof Uint8ClampedArray) || - (o instanceof Int16Array) || - (o instanceof Uint16Array) || - (o instanceof Int32Array) || - (o instanceof Uint32Array) || - (o instanceof Float32Array) || - (o instanceof Float64Array) || - (o instanceof Float32x4Array); +/** + * @return {float32x4} New instance of float32x4 with square root of the + * reciprocal value of t. + */ +SIMD.float32x4.reciprocalSqrt = function(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)); +} +/** + * @return {float32x4} New instance of float32x4 with values of t + * scaled by s. + */ +SIMD.float32x4.scale = function(t, s) { + return SIMD.float32x4(s * t.x, s * t.y, s * t.z, s * t.w); } -function isArrayBuffer(o) { - return (o instanceof ArrayBuffer); +/** + * @return {float32x4} New instance of float32x4 with square root of + * values of t. + */ +SIMD.float32x4.sqrt = function(t) { + return SIMD.float32x4(Math.sqrt(t.x), Math.sqrt(t.y), + Math.sqrt(t.z), Math.sqrt(t.w)); } -function Float32x4Array(a, b, c) { - if (isNumber(a)) { - this.storage_ = new Float32Array(a*4); - this.length_ = a; - this.byteOffset_ = 0; - return; - } else if (isTypedArray(a)) { - if (!(a instanceof Float32x4Array)) { - throw "Copying typed array of non-Float32x4Array is unimplemented."; - } - this.storage_ = new Float32Array(a.length * 4); - this.length_ = a.length; - this.byteOffset_ = 0; - // Copy floats. - for (var i = 0; i < a.length*4; i++) { - this.storage_[i] = a.storage_[i]; - } - } else if (isArrayBuffer(a)) { - if ((b != undefined) && (b % Float32x4Array.BYTES_PER_ELEMENT) != 0) { - throw "byteOffset must be a multiple of 16."; - } - if (c != undefined) { - c *= 4; - this.storage_ = new Float32Array(a, b, c); - } - else { - // Note: new Float32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined) - this.storage_ = new Float32Array(a, b); - } - this.length_ = this.storage_.length / 4; - this.byteOffset_ = b != undefined ? b : 0; - } else { - throw "Unknown type of first argument."; - } +/** + * @param {float32x4} t An instance of float32x4 to be shuffled. + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {float32x4} New instance of float32x4 with lanes shuffled. + */ +SIMD.float32x4.shuffle = function(t, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return SIMD.float32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z], + t.storage_[_w]); } -Object.defineProperty(Float32x4Array.prototype, 'length', - { get: function() { return this.length_; } -}); +/** + * @param {float32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result + * @param {float32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {float32x4} New instance of float32x4 with lanes shuffled. + */ +SIMD.float32x4.shuffleMix = function(t1, t2, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return SIMD.float32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z], + t2.storage_[_w]); +} -Object.defineProperty(Float32x4Array.prototype, 'byteLength', - { get: function() { return this.length_ * Float32x4Array.BYTES_PER_ELEMENT; } -}); +/** + * @param {double} value used for x lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * x replaced with {x}. + */ +SIMD.float32x4.withX = function(t, x) { + return SIMD.float32x4(x, t.y, t.z, t.w); +} -Object.defineProperty(Float32x4Array, 'BYTES_PER_ELEMENT', - { get: function() { return 16; } -}); +/** + * @param {double} value used for y lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * y replaced with {y}. + */ +SIMD.float32x4.withY = function(t, y) { + return SIMD.float32x4(t.x, y, t.z, t.w); +} -Object.defineProperty(Float32x4Array.prototype, 'BYTES_PER_ELEMENT', - { get: function() { return 16; } -}); +/** + * @param {double} value used for z lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * z replaced with {z}. + */ +SIMD.float32x4.withZ = function(t, z) { + return SIMD.float32x4(t.x, t.y, z, t.w); +} -Object.defineProperty(Float32x4Array.prototype, 'byteOffset', - { get: function() { return this.byteOffset_; } -}); +/** + * @param {double} value used for w lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * w replaced with {w}. + */ +SIMD.float32x4.withW = function(t, w) { + return SIMD.float32x4(t.x, t.y, t.z, w); +} -Object.defineProperty(Float32x4Array.prototype, 'buffer', - { get: function() { return this.storage_.buffer; } -}); +/** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t < other. + */ +SIMD.float32x4.lessThan = function(t, other) { + var cx = t.x < other.x; + var cy = t.y < other.y; + var cz = t.z < other.z; + var cw = t.w < other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); +} -Float32x4Array.prototype.getAt = function(i) { - if (i < 0) { - throw "Index must be >= 0."; - } - if (i >= this.length) { - throw "Index out of bounds."; - } - var x = this.storage_[i*4+0]; - var y = this.storage_[i*4+1]; - var z = this.storage_[i*4+2]; - var w = this.storage_[i*4+3]; - return float32x4(x, y, z, w); +/** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t <= other. + */ +SIMD.float32x4.lessThanOrEqual = function(t, other) { + var cx = t.x <= other.x; + var cy = t.y <= other.y; + var cz = t.z <= other.z; + var cw = t.w <= other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); } -Float32x4Array.prototype.setAt = function(i, v) { - if (i < 0) { - throw "Index must be >= 0."; - } - if (i >= this.length) { - throw "Index out of bounds."; - } - if (!(v instanceof float32x4)) { - throw "Value is not a float32x4."; - } - this.storage_[i*4+0] = v.x; - this.storage_[i*4+1] = v.y; - this.storage_[i*4+2] = v.z; - this.storage_[i*4+3] = v.w; +/** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t == other. + */ +SIMD.float32x4.equal = function(t, other) { + var cx = t.x == other.x; + var cy = t.y == other.y; + var cz = t.z == other.z; + var cw = t.w == other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); } +/** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t != other. + */ +SIMD.float32x4.notEqual = function(t, other) { + var cx = t.x != other.x; + var cy = t.y != other.y; + var cz = t.z != other.z; + var cw = t.w != other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); +} -function Int32x4Array(a, b, c) { +/** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t >= other. + */ +SIMD.float32x4.greaterThanOrEqual = function(t, other) { + var cx = t.x >= other.x; + var cy = t.y >= other.y; + var cz = t.z >= other.z; + var cw = t.w >= other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); +} - function isNumber(o) { - return typeof o == "number" || (typeof o == "object" && o.constructor === Number); - } +/** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t > other. + */ +SIMD.float32x4.greaterThan = function(t, other) { + var cx = t.x > other.x; + var cy = t.y > other.y; + var cz = t.z > other.z; + var cw = t.w > other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); +} - function isTypedArray(o) { - return (o instanceof Int8Array) || - (o instanceof Uint8Array) || - (o instanceof Uint8ClampedArray) || - (o instanceof Int16Array) || - (o instanceof Uint16Array) || - (o instanceof Int32Array) || - (o instanceof Uint32Array) || - (o instanceof Float32Array) || - (o instanceof Float64Array) || - (o instanceof Int32x4Array) || - (o instanceof Float32x4Array); - } +/** + * @param {float32x4} t An instance of float32x4. + * @return {int32x4} a bit-wise copy of t as a int32x4. + */ +SIMD.float32x4.bitsToInt32x4 = function(t) { + var alias = new Int32Array(t.storage_.buffer); + return SIMD.int32x4(alias[0], alias[1], alias[2], alias[3]); +} - function isArrayBuffer(o) { - return (o instanceof ArrayBuffer); - } +/** + * @param {float32x4} t An instance of float32x4. + * @return {int32x4} with a integer to float conversion of t. + */ +SIMD.float32x4.toInt32x4 = function(t) { + var a = SIMD.int32x4(t.storage_[0], t.storage_[1], t.storage_[2], + t.storage_[3]); + return a; +} - if (isNumber(a)) { - this.storage_ = new Int32Array(a*4); - this.length_ = a; - this.byteOffset_ = 0; - return; - } else if (isTypedArray(a)) { - if (!(a instanceof Int32x4Array)) { - throw "Copying typed array of non-Int32x4Array is unimplemented."; - } - this.storage_ = new Int32Array(a.length * 4); - this.length_ = a.length; - this.byteOffset_ = 0; - // Copy floats. - for (var i = 0; i < a.length*4; i++) { - this.storage_[i] = a.storage_[i]; - } - } else if (isArrayBuffer(a)) { - if ((b != undefined) && (b % Int32x4Array.BYTES_PER_ELEMENT) != 0) { - throw "byteOffset must be a multiple of 16."; - } - if (c != undefined) { - c *= 4; - this.storage_ = new Int32Array(a, b, c); - } - else { - // Note: new Int32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined) - this.storage_ = new Int32Array(a, b); - } - this.length_ = this.storage_.length / 4; - this.byteOffset_ = b != undefined ? b : 0; - } else { - throw "Unknown type of first argument."; - } +/** + * @param {float32x4} a An instance of float32x4. + * @param {float32x4} b An instance of float32x4. + * @return {float32x4} New instance of float32x4 with values of a & b. + */ +SIMD.float32x4.and = function(a, b) { + var aInt = SIMD.float32x4.bitsToInt32x4(a); + var bInt = SIMD.float32x4.bitsToInt32x4(b); + return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(aInt, bInt)); } -Object.defineProperty(Int32x4Array.prototype, 'length', - { get: function() { return this.length_; } -}); +/** + * @param {float32x4} a An instance of float32x4. + * @param {float32x4} b An instance of float32x4. + * @return {float32x4} New instance of float32x4 with values of a | b. + */ +SIMD.float32x4.or = function(a, b) { + var aInt = SIMD.float32x4.bitsToInt32x4(a); + var bInt = SIMD.float32x4.bitsToInt32x4(b); + return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(aInt, bInt)); +} -Object.defineProperty(Int32x4Array.prototype, 'byteLength', - { get: function() { return this.length_ * Int32x4Array.BYTES_PER_ELEMENT; } -}); +/** + * @param {float32x4} a An instance of float32x4. + * @param {float32x4} b An instance of float32x4. + * @return {float32x4} New instance of float32x4 with values of a ^ b. + */ +SIMD.float32x4.xor = function(a, b) { + var aInt = SIMD.float32x4.bitsToInt32x4(a); + var bInt = SIMD.float32x4.bitsToInt32x4(b); + return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.xor(aInt, bInt)); +} -Object.defineProperty(Int32x4Array, 'BYTES_PER_ELEMENT', - { get: function() { return 16; } -}); +/** + * @param {float32x4} a An instance of float32x4. + * @return {float32x4} New instance of float32x4 with values of ~a. + */ +SIMD.float32x4.not = function(a) { + var aInt = SIMD.float32x4.bitsToInt32x4(a); + return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.not(aInt)); +} -Object.defineProperty(Int32x4Array.prototype, 'BYTES_PER_ELEMENT', - { get: function() { return 16; } -}); +/** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a & b. + */ +SIMD.int32x4.and = function(a, b) { + return SIMD.int32x4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); +} -Object.defineProperty(Int32x4Array.prototype, 'byteOffset', - { get: function() { return this.byteOffset_; } -}); +/** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a | b. + */ +SIMD.int32x4.or = function(a, b) { + return SIMD.int32x4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); +} -Object.defineProperty(Int32x4Array.prototype, 'buffer', - { get: function() { return this.storage_.buffer; } -}); +/** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a ^ b. + */ +SIMD.int32x4.xor = function(a, b) { + return SIMD.int32x4(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); +} -Int32x4Array.prototype.getAt = function(i) { - if (i < 0) { - throw "Index must be >= 0."; - } - if (i >= this.length) { - throw "Index out of bounds."; - } - var x = this.storage_[i*4+0]; - var y = this.storage_[i*4+1]; - var z = this.storage_[i*4+2]; - var w = this.storage_[i*4+3]; - return float32x4(x, y, z, w); +/** + * @param {int32x4} t An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of ~t + */ +SIMD.int32x4.not = function(t) { + return SIMD.int32x4(~t.x, ~t.y, ~t.z, ~t.w); } -Int32x4Array.prototype.setAt = function(i, v) { - if (i < 0) { - throw "Index must be >= 0."; - } - if (i >= this.length) { - throw "Index out of bounds."; - } - if (!(v instanceof int32x4)) { - throw "Value is not a int32x4."; - } - this.storage_[i*4+0] = v.x; - this.storage_[i*4+1] = v.y; - this.storage_[i*4+2] = v.z; - this.storage_[i*4+3] = v.w; +/** + * @param {int32x4} t An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of -t + */ +SIMD.int32x4.neg = function(t) { + return SIMD.int32x4(-t.x, -t.y, -t.z, -t.w); } -var SIMD = (function () { - return { - float32x4: { - /** - * @return {float32x4} New instance of float32x4 with absolute values of - * t. - */ - abs: function(t) { - return new float32x4(Math.abs(t.x), Math.abs(t.y), Math.abs(t.z), - Math.abs(t.w)); - }, - /** - * @return {float32x4} New instance of float32x4 with negated values of - * t. - */ - neg: function(t) { - return new float32x4(-t.x, -t.y, -t.z, -t.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a + b. - */ - add: function(a, b) { - return new float32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a - b. - */ - sub: function(a, b) { - return new float32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a * b. - */ - mul: function(a, b) { - return new float32x4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a / b. - */ - div: function(a, b) { - return new float32x4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with t's values clamped - * between lowerLimit and upperLimit. - */ - clamp: function(t, lowerLimit, 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; - var cw = t.w < lowerLimit.w ? lowerLimit.w : t.w; - cx = cx > upperLimit.x ? upperLimit.x : cx; - cy = cy > upperLimit.y ? upperLimit.y : cy; - cz = cz > upperLimit.z ? upperLimit.z : cz; - cw = cw > upperLimit.w ? upperLimit.w : cw; - return new float32x4(cx, cy, cz, cw); - }, - /** - * @return {float32x4} New instance of float32x4 with the minimum value of - * t and other. - */ - min: function(t, other) { - var cx = t.x > other.x ? other.x : t.x; - var cy = t.y > other.y ? other.y : t.y; - var cz = t.z > other.z ? other.z : t.z; - var cw = t.w > other.w ? other.w : t.w; - return new float32x4(cx, cy, cz, cw); - }, - /** - * @return {float32x4} New instance of float32x4 with the maximum value of - * t and other. - */ - max: function(t, other) { - var cx = t.x < other.x ? other.x : t.x; - var cy = t.y < other.y ? other.y : t.y; - var cz = t.z < other.z ? other.z : t.z; - var cw = t.w < other.w ? other.w : t.w; - return new float32x4(cx, cy, cz, cw); - }, - /** - * @return {float32x4} New instance of float32x4 with reciprocal value of - * t. - */ - reciprocal: function(t) { - return new float32x4(1.0 / t.x, 1.0 / t.y, 1.0 / t.z, 1.0 / t.w); - }, - /** - * @return {float32x4} New instance of float32x4 with square root of the - * reciprocal value of t. - */ - reciprocalSqrt: function(t) { - return new 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)); - }, - /** - * @return {float32x4} New instance of float32x4 with values of t - * scaled by s. - */ - scale: function(t, s) { - return new float32x4(s * t.x, s * t.y, s * t.z, s * t.w); - }, - /** - * @return {float32x4} New instance of float32x4 with square root of - * values of t. - */ - sqrt: function(t) { - return new float32x4(Math.sqrt(t.x), Math.sqrt(t.y), - Math.sqrt(t.z), Math.sqrt(t.w)); - }, - /** - * @param {float32x4} t An instance of float32x4 to be shuffled. - * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. - * @return {float32x4} New instance of float32x4 with lanes shuffled. - */ - shuffle: function(t, mask) { - var _x = (mask) & 0x3; - var _y = (mask >> 2) & 0x3; - var _z = (mask >> 4) & 0x3; - var _w = (mask >> 6) & 0x3; - return new float32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z], - t.storage_[_w]); - }, - /** - * @param {float32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result - * @param {float32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result - * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. - * @return {float32x4} New instance of float32x4 with lanes shuffled. - */ - shuffleMix: function(t1, t2, mask) { - var _x = (mask) & 0x3; - var _y = (mask >> 2) & 0x3; - var _z = (mask >> 4) & 0x3; - var _w = (mask >> 6) & 0x3; - return new float32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z], - t2.storage_[_w]); - }, - /** - * @param {double} value used for x lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * x replaced with {x}. - */ - withX: function(t, x) { - return new float32x4(x, t.y, t.z, t.w); - }, - /** - * @param {double} value used for y lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * y replaced with {y}. - */ - withY: function(t, y) { - return new float32x4(t.x, y, t.z, t.w); - }, - /** - * @param {double} value used for z lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * z replaced with {z}. - */ - withZ: function(t, z) { - return new float32x4(t.x, t.y, z, t.w); - }, - /** - * @param {double} value used for w lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * w replaced with {w}. - */ - withW: function(t, w) { - return new float32x4(t.x, t.y, t.z, w); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @param {float32x4} other An instance of float32x4. - * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t < other. - */ - lessThan: function(t, other) { - var cx = t.x < other.x; - var cy = t.y < other.y; - var cz = t.z < other.z; - var cw = t.w < other.w; - return int32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @param {float32x4} other An instance of float32x4. - * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t <= other. - */ - lessThanOrEqual: function(t, other) { - var cx = t.x <= other.x; - var cy = t.y <= other.y; - var cz = t.z <= other.z; - var cw = t.w <= other.w; - return int32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @param {float32x4} other An instance of float32x4. - * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t == other. - */ - equal: function(t, other) { - var cx = t.x == other.x; - var cy = t.y == other.y; - var cz = t.z == other.z; - var cw = t.w == other.w; - return int32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @param {float32x4} other An instance of float32x4. - * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t != other. - */ - notEqual: function(t, other) { - var cx = t.x != other.x; - var cy = t.y != other.y; - var cz = t.z != other.z; - var cw = t.w != other.w; - return int32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @param {float32x4} other An instance of float32x4. - * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t >= other. - */ - greaterThanOrEqual: function(t, other) { - var cx = t.x >= other.x; - var cy = t.y >= other.y; - var cz = t.z >= other.z; - var cw = t.w >= other.w; - return int32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @param {float32x4} other An instance of float32x4. - * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t > other. - */ - greaterThan: function(t, other) { - var cx = t.x > other.x; - var cy = t.y > other.y; - var cz = t.z > other.z; - var cw = t.w > other.w; - return int32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @return {int32x4} a bit-wise copy of t as a int32x4. - */ - bitsToInt32x4: function(t) { - var alias = new Int32Array(t.storage_.buffer); - return new int32x4(alias[0], alias[1], alias[2], alias[3]); - }, - /** - * @param {float32x4} t An instance of float32x4. - * @return {int32x4} with a integer to float conversion of t. - */ - toInt32x4: function(t) { - var a = new int32x4(t.storage_[0], t.storage_[1], t.storage_[2], - t.storage_[3]); - return a; - } - }, - int32x4: { - /** - * @param {int32x4} a An instance of int32x4. - * @param {int32x4} b An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of a & b. - */ - and: function(a, b) { - return new int32x4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); - }, - /** - * @param {int32x4} a An instance of int32x4. - * @param {int32x4} b An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of a | b. - */ - or: function(a, b) { - return new int32x4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); - }, - /** - * @param {int32x4} a An instance of int32x4. - * @param {int32x4} b An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of a ^ b. - */ - xor: function(a, b) { - return new int32x4(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of ~t - */ - not: function(t) { - return new int32x4(~t.x, ~t.y, ~t.z, ~t.w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of -t - */ - neg: function(t) { - return new int32x4(-t.x, -t.y, -t.z, -t.w); - }, - /** - * @param {int32x4} a An instance of int32x4. - * @param {int32x4} b An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of a + b. - */ - add: function(a, b) { - return new int32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); - }, - /** - * @param {int32x4} a An instance of int32x4. - * @param {int32x4} b An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of a - b. - */ - sub: function(a, b) { - return new int32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); - }, - /** - * @param {int32x4} a An instance of int32x4. - * @param {int32x4} b An instance of int32x4. - * @return {int32x4} New instance of int32x4 with values of a * b. - */ - mul: function(a, b) { - return new 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)); - }, - /** - * @param {int32x4} t An instance of float32x4 to be shuffled. - * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. - * @return {int32x4} New instance of float32x4 with lanes shuffled. - */ - shuffle: function(t, mask) { - var _x = (mask) & 0x3; - var _y = (mask >> 2) & 0x3; - var _z = (mask >> 4) & 0x3; - var _w = (mask >> 6) & 0x3; - return new int32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z], - t.storage_[_w]); - }, - /** - * @param {int32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result - * @param {int32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result - * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. - * @return {int32x4} New instance of float32x4 with lanes shuffled. - */ - shuffleMix: function(t1, t2, mask) { - var _x = (mask) & 0x3; - var _y = (mask >> 2) & 0x3; - var _z = (mask >> 4) & 0x3; - var _w = (mask >> 6) & 0x3; - return new int32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z], - t2.storage_[_w]); - }, - /** - * @param {float32x4} - */ - select: function(t, trueValue, falseValue) { - var tv = SIMD.float32x4.bitsToInt32x4(trueValue); - var fv = SIMD.float32x4.bitsToInt32x4(falseValue); - var tr = SIMD.int32x4.and(t, tv); - var fr = SIMD.int32x4.and(SIMD.int32x4.not(t), fv); - return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(tr, fr)); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @param {integer} 32-bit value used for x lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * x lane replaced with {x}. - */ - withX: function(t, x) { - return new int32x4(x, t.y, t.z, t.w); - }, - /** - * param {int32x4} t An instance of int32x4. - * @param {integer} 32-bit value used for y lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * y lane replaced with {y}. - */ - withY: function(t, y) { - return new int32x4(t.x, y, t.z, t.w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @param {integer} 32-bit value used for z lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * z lane replaced with {z}. - */ - withZ: function(t, z) { - return new int32x4(t.x, t.y, z, t.w); - }, - /** - * @param {integer} 32-bit value used for w lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * w lane replaced with {w}. - */ - withW: function(t, w) { - return new int32x4(t.x, t.y, t.z, w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @param {boolean} x flag used for x lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * x lane replaced with {x}. - */ - withFlagX: function(t, flagX) { - var x = flagX ? 0xFFFFFFFF : 0x0; - return new int32x4(x, t.y, t.z, t.w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @param {boolean} y flag used for y lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * y lane replaced with {y}. - */ - withFlagY: function(t, flagY) { - var y = flagY ? 0xFFFFFFFF : 0x0; - return new int32x4(t.x, y, t.z, t.w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @param {boolean} z flag used for z lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * z lane replaced with {z}. - */ - withFlagZ: function(t, flagZ) { - var z = flagZ ? 0xFFFFFFFF : 0x0; - return new int32x4(t.x, t.y, z, t.w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @param {boolean} w flag used for w lane. - * @return {int32x4} New instance of int32x4 with the values in t and - * w lane replaced with {w}. - */ - withFlagW: function(t, flagW) { - var w = flagW ? 0xFFFFFFFF : 0x0; - return new int32x4(t.x, t.y, t.z, w); - }, - /** - * @param {int32x4} t An instance of int32x4. - * @return {float32x4} a bit-wise copy of t as a float32x4. - */ - bitsToFloat32x4: function(t) { - var temp_storage = new Int32Array([t.storage_[0], t.storage_[1], t.storage_[2], t.storage_[3]]); - var alias = new Float32Array(temp_storage.buffer); - var fx4 = float32x4.zero(); - fx4.storage_ = alias; - return fx4; - }, - /** - * @param {int32x4} t An instance of int32x4. - * @return {float32x4} with a float to integer conversion copy of t. - */ - toFloat32x4: function(t) { - var a = float32x4.zero(); - a.storage_[0] = t.storage_[0]; - a.storage_[1] = t.storage_[1]; - a.storage_[2] = t.storage_[2]; - a.storage_[3] = t.storage_[3]; - return a; - } - } - } -})(); +/** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a + b. + */ +SIMD.int32x4.add = function(a, b) { + return SIMD.int32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} + +/** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a - b. + */ +SIMD.int32x4.sub = function(a, b) { + return SIMD.int32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} + +/** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a * b. + */ +SIMD.int32x4.mul = function(a, 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)); +} + +/** + * @param {int32x4} t An instance of float32x4 to be shuffled. + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {int32x4} New instance of float32x4 with lanes shuffled. + */ +SIMD.int32x4.shuffle = function(t, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return SIMD.int32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z], + t.storage_[_w]); +} + +/** + * @param {int32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result + * @param {int32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {int32x4} New instance of float32x4 with lanes shuffled. + */ +SIMD.int32x4.shuffleMix = function(t1, t2, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return SIMD.int32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z], + t2.storage_[_w]); +} + +/** + * @param {float32x4} + */ +SIMD.int32x4.select = function(t, trueValue, falseValue) { + var tv = SIMD.float32x4.bitsToInt32x4(trueValue); + var fv = SIMD.float32x4.bitsToInt32x4(falseValue); + var tr = SIMD.int32x4.and(t, tv); + var fr = SIMD.int32x4.and(SIMD.int32x4.not(t), fv); + return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(tr, fr)); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {integer} 32-bit value used for x lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * x lane replaced with {x}. + */ +SIMD.int32x4.withX = function(t, x) { + return SIMD.int32x4(x, t.y, t.z, t.w); +} + +/** + * param {int32x4} t An instance of int32x4. + * @param {integer} 32-bit value used for y lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * y lane replaced with {y}. + */ +SIMD.int32x4.withY = function(t, y) { + return SIMD.int32x4(t.x, y, t.z, t.w); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {integer} 32-bit value used for z lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * z lane replaced with {z}. + */ +SIMD.int32x4.withZ = function(t, z) { + return SIMD.int32x4(t.x, t.y, z, t.w); +} + +/** + * @param {integer} 32-bit value used for w lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * w lane replaced with {w}. + */ +SIMD.int32x4.withW = function(t, w) { + return SIMD.int32x4(t.x, t.y, t.z, w); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} x flag used for x lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * x lane replaced with {x}. + */ +SIMD.int32x4.withFlagX = function(t, flagX) { + var x = flagX ? 0xFFFFFFFF : 0x0; + return SIMD.int32x4(x, t.y, t.z, t.w); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} y flag used for y lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * y lane replaced with {y}. + */ +SIMD.int32x4.withFlagY = function(t, flagY) { + var y = flagY ? 0xFFFFFFFF : 0x0; + return SIMD.int32x4(t.x, y, t.z, t.w); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} z flag used for z lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * z lane replaced with {z}. + */ +SIMD.int32x4.withFlagZ = function(t, flagZ) { + var z = flagZ ? 0xFFFFFFFF : 0x0; + return SIMD.int32x4(t.x, t.y, z, t.w); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} w flag used for w lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * w lane replaced with {w}. + */ +SIMD.int32x4.withFlagW = function(t, flagW) { + var w = flagW ? 0xFFFFFFFF : 0x0; + return SIMD.int32x4(t.x, t.y, t.z, w); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {int32x4} other An instance of int32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t == other. + */ +SIMD.int32x4.equal = function(t, other) { + var cx = t.x == other.x; + var cy = t.y == other.y; + var cz = t.z == other.z; + var cw = t.w == other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {int32x4} other An instance of int32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t > other. + */ +SIMD.int32x4.greaterThan = function(t, other) { + var cx = t.x > other.x; + var cy = t.y > other.y; + var cz = t.z > other.z; + var cw = t.w > other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @param {int32x4} other An instance of int32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t < other. + */ +SIMD.int32x4.lessThan = function(t, other) { + var cx = t.x < other.x; + var cy = t.y < other.y; + var cz = t.z < other.z; + var cw = t.w < other.w; + return SIMD.int32x4.bool(cx, cy, cz, cw); +} + +/** + * @param {int32x4} a An instance of int32x4. + * @param {int} bits Bit count to shift by. + * @return {int32x4} lanes in a shifted by bits. + */ +SIMD.int32x4.shiftLeft = function(a, bits) { + var x = a.x << bits; + var y = a.y << bits; + var z = a.z << bits; + var w = a.w << bits; + return SIMD.int32x4(x, y, z, w); +} + +/** + * @param {int32x4} a An instance of int32x4. + * @param {int} bits Bit count to shift by. + * @return {int32x4} lanes in a shifted by bits. + */ +SIMD.int32x4.shiftRightLogical = function(a, bits) { + var x = a.x >>> bits; + var y = a.y >>> bits; + var z = a.z >>> bits; + var w = a.w >>> bits; + return SIMD.int32x4(x, y, z, w); +} + +/** + * @param {int32x4} a An instance of int32x4. + * @param {int} bits Bit count to shift by. + * @return {int32x4} lanes in a shifted by bits. + */ +SIMD.int32x4.shiftRightArithmetic = function(a, bits) { + var x = a.x >> bits; + var y = a.y >> bits; + var z = a.z >> bits; + var w = a.w >> bits; + return SIMD.int32x4(x, y, z, w); +} + +/** + * @param {int32x4} t An instance of int32x4. + * @return {float32x4} a bit-wise copy of t as a float32x4. + */ +SIMD.int32x4.bitsToFloat32x4 = function(t) { + var temp_storage = new Int32Array([t.storage_[0], t.storage_[1], t.storage_[2], t.storage_[3]]); + var alias = new Float32Array(temp_storage.buffer); + var fx4 = SIMD.float32x4.zero(); + fx4.storage_ = alias; + return fx4; +} + +/** + * @param {int32x4} t An instance of int32x4. + * @return {float32x4} with a float to integer conversion copy of t. + */ +SIMD.int32x4.toFloat32x4 = function(t) { + var a = float32x4.zero(); + a.storage_[0] = t.storage_[0]; + a.storage_[1] = t.storage_[1]; + a.storage_[2] = t.storage_[2]; + a.storage_[3] = t.storage_[3]; + return a; +} Object.defineProperty(SIMD, 'XXXX', { get: function() { return 0x0; } }); Object.defineProperty(SIMD, 'XXXY', { get: function() { return 0x40; } }); @@ -1126,3 +1000,302 @@ Object.defineProperty(SIMD, 'WWWX', { get: function() { return 0x3F; } }); Object.defineProperty(SIMD, 'WWWY', { get: function() { return 0x7F; } }); Object.defineProperty(SIMD, 'WWWZ', { get: function() { return 0xBF; } }); Object.defineProperty(SIMD, 'WWWW', { get: function() { return 0xFF; } }); + +Object.defineProperty(SIMD.float32x4.prototype, 'x', { + get: function() { return this.storage_[0]; } +}); + +Object.defineProperty(SIMD.float32x4.prototype, 'y', { + get: function() { return this.storage_[1]; } +}); + +Object.defineProperty(SIMD.float32x4.prototype, 'z', { + get: function() { return this.storage_[2]; } +}); + +Object.defineProperty(SIMD.float32x4.prototype, 'w', + { get: function() { return this.storage_[3]; } +}); + +/** + * Extract the sign bit from each lane return them in the first 4 bits. + */ +Object.defineProperty(SIMD.float32x4.prototype, 'signMask', { + get: function() { + var mx = this.x < 0.0 ? 1 : 0; + var my = this.y < 0.0 ? 1 : 0; + var mz = this.z < 0.0 ? 1 : 0; + var mw = this.w < 0.0 ? 1 : 0; + return mx | my << 1 | mz << 2 | mw << 3; + } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'x', { + get: function() { return this.storage_[0]; } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'y', { + get: function() { return this.storage_[1]; } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'z', { + get: function() { return this.storage_[2]; } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'w', + { get: function() { return this.storage_[3]; } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'flagX', { + get: function() { return this.storage_[0] != 0x0; } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'flagY', { + get: function() { return this.storage_[1] != 0x0; } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'flagZ', { + get: function() { return this.storage_[2] != 0x0; } +}); + +Object.defineProperty(SIMD.int32x4.prototype, 'flagW', + { get: function() { return this.storage_[3] != 0x0; } +}); + +/** + * Extract the sign bit from each lane return them in the first 4 bits. + */ +Object.defineProperty(SIMD.int32x4.prototype, 'signMask', { + get: function() { + var mx = (this.storage_[0] & 0x80000000) >>> 31; + var my = (this.storage_[1] & 0x80000000) >>> 31; + var mz = (this.storage_[2] & 0x80000000) >>> 31; + var mw = (this.storage_[3] & 0x80000000) >>> 31; + return mx | my << 1 | mz << 2 | mw << 3; + } +}); + +function isNumber(o) { + return typeof o == "number" || (typeof o == "object" && o.constructor === Number); +} + +function isTypedArray(o) { + return (o instanceof Int8Array) || + (o instanceof Uint8Array) || + (o instanceof Uint8ClampedArray) || + (o instanceof Int16Array) || + (o instanceof Uint16Array) || + (o instanceof Int32Array) || + (o instanceof Uint32Array) || + (o instanceof Float32Array) || + (o instanceof Float64Array) || + (o instanceof Float32x4Array); +} + +function isArrayBuffer(o) { + return (o instanceof ArrayBuffer); +} + +function Float32x4Array(a, b, c) { + if (isNumber(a)) { + this.storage_ = new Float32Array(a*4); + this.length_ = a; + this.byteOffset_ = 0; + return; + } else if (isTypedArray(a)) { + if (!(a instanceof Float32x4Array)) { + throw "Copying typed array of non-Float32x4Array is unimplemented."; + } + this.storage_ = new Float32Array(a.length * 4); + this.length_ = a.length; + this.byteOffset_ = 0; + // Copy floats. + for (var i = 0; i < a.length*4; i++) { + this.storage_[i] = a.storage_[i]; + } + } else if (isArrayBuffer(a)) { + if ((b != undefined) && (b % Float32x4Array.BYTES_PER_ELEMENT) != 0) { + throw "byteOffset must be a multiple of 16."; + } + if (c != undefined) { + c *= 4; + this.storage_ = new Float32Array(a, b, c); + } + else { + // Note = new Float32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined) + this.storage_ = new Float32Array(a, b); + } + this.length_ = this.storage_.length / 4; + this.byteOffset_ = b != undefined ? b : 0; + } else { + throw "Unknown type of first argument."; + } +} + +Object.defineProperty(Float32x4Array.prototype, 'length', + { get: function() { return this.length_; } +}); + +Object.defineProperty(Float32x4Array.prototype, 'byteLength', + { get: function() { return this.length_ * Float32x4Array.BYTES_PER_ELEMENT; } +}); + +Object.defineProperty(Float32x4Array, 'BYTES_PER_ELEMENT', + { get: function() { return 16; } +}); + +Object.defineProperty(Float32x4Array.prototype, 'BYTES_PER_ELEMENT', + { get: function() { return 16; } +}); + +Object.defineProperty(Float32x4Array.prototype, 'byteOffset', + { get: function() { return this.byteOffset_; } +}); + +Object.defineProperty(Float32x4Array.prototype, 'buffer', + { get: function() { return this.storage_.buffer; } +}); + +Float32x4Array.prototype.getAt = function(i) { + if (i < 0) { + throw "Index must be >= 0."; + } + if (i >= this.length) { + throw "Index out of bounds."; + } + var x = this.storage_[i*4+0]; + var y = this.storage_[i*4+1]; + var z = this.storage_[i*4+2]; + var w = this.storage_[i*4+3]; + return SIMD.float32x4(x, y, z, w); +} + +Float32x4Array.prototype.setAt = function(i, v) { + if (i < 0) { + throw "Index must be >= 0."; + } + if (i >= this.length) { + throw "Index out of bounds."; + } + if (!(v instanceof SIMD.float32x4)) { + throw "Value is not a float32x4."; + } + this.storage_[i*4+0] = v.x; + this.storage_[i*4+1] = v.y; + this.storage_[i*4+2] = v.z; + this.storage_[i*4+3] = v.w; +} + + +function Int32x4Array(a, b, c) { + + function isNumber(o) { + return typeof o == "number" || (typeof o == "object" && o.constructor === Number); + } + + function isTypedArray(o) { + return (o instanceof Int8Array) || + (o instanceof Uint8Array) || + (o instanceof Uint8ClampedArray) || + (o instanceof Int16Array) || + (o instanceof Uint16Array) || + (o instanceof Int32Array) || + (o instanceof Uint32Array) || + (o instanceof Float32Array) || + (o instanceof Float64Array) || + (o instanceof Int32x4Array) || + (o instanceof Float32x4Array); + } + + function isArrayBuffer(o) { + return (o instanceof ArrayBuffer); + } + + if (isNumber(a)) { + this.storage_ = new Int32Array(a*4); + this.length_ = a; + this.byteOffset_ = 0; + return; + } else if (isTypedArray(a)) { + if (!(a instanceof Int32x4Array)) { + throw "Copying typed array of non-Int32x4Array is unimplemented."; + } + this.storage_ = new Int32Array(a.length * 4); + this.length_ = a.length; + this.byteOffset_ = 0; + // Copy ints. + for (var i = 0; i < a.length*4; i++) { + this.storage_[i] = a.storage_[i]; + } + } else if (isArrayBuffer(a)) { + if ((b != undefined) && (b % Int32x4Array.BYTES_PER_ELEMENT) != 0) { + throw "byteOffset must be a multiple of 16."; + } + if (c != undefined) { + c *= 4; + this.storage_ = new Int32Array(a, b, c); + } + else { + // Note = new Int32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined) + this.storage_ = new Int32Array(a, b); + } + this.length_ = this.storage_.length / 4; + this.byteOffset_ = b != undefined ? b : 0; + } else { + throw "Unknown type of first argument."; + } +} + +Object.defineProperty(Int32x4Array.prototype, 'length', + { get: function() { return this.length_; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'byteLength', + { get: function() { return this.length_ * Int32x4Array.BYTES_PER_ELEMENT; } +}); + +Object.defineProperty(Int32x4Array, 'BYTES_PER_ELEMENT', + { get: function() { return 16; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'BYTES_PER_ELEMENT', + { get: function() { return 16; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'byteOffset', + { get: function() { return this.byteOffset_; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'buffer', + { get: function() { return this.storage_.buffer; } +}); + +Int32x4Array.prototype.getAt = function(i) { + if (i < 0) { + throw "Index must be >= 0."; + } + if (i >= this.length) { + throw "Index out of bounds."; + } + var x = this.storage_[i*4+0]; + var y = this.storage_[i*4+1]; + var z = this.storage_[i*4+2]; + var w = this.storage_[i*4+3]; + return SIMD.int32x4(x, y, z, w); +} + +Int32x4Array.prototype.setAt = function(i, v) { + if (i < 0) { + throw "Index must be >= 0."; + } + if (i >= this.length) { + throw "Index out of bounds."; + } + if (!(v instanceof SIMD.int32x4)) { + throw "Value is not a int32x4."; + } + this.storage_[i*4+0] = v.x; + this.storage_[i*4+1] = v.y; + this.storage_[i*4+2] = v.z; + this.storage_[i*4+3] = v.w; +} \ No newline at end of file diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 8a08aabb87695..b495c0f559e16 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -14,6 +14,8 @@ extern "C" { #endif +#include + #if !__EMSCRIPTEN__ #include /* for SDL_Delay in async_call */ #endif @@ -53,8 +55,8 @@ typedef double __attribute__((aligned(1))) emscripten_align1_double; * does a function call to reach it). It supports newlines, * * EM_ASM( - * window.alert('hai')); - * window.alert('bai')); + * window.alert('hai'); + * window.alert('bai'); * ) * * Notes: Double-quotes (") are not supported, but you can use @@ -515,6 +517,24 @@ int emscripten_get_compiler_setting(const char *name); */ void emscripten_debugger(); +/* + * Get preloaded image data and the size of the image. + * + * Returns pointer to loaded image or NULL. + * width/height of image are written to w/h if data is valid. + * Pointer should be free()'d + */ +char *emscripten_get_preloaded_image_data(const char *path, int *w, int *h); + +/* + * Get preloaded image data from a c FILE *. + * + * Returns pointer to loaded image or NULL. + * width/height of image are written to w/h if data is valid. + * Pointer should be free()'d + */ +char *emscripten_get_preloaded_image_data_from_FILE(FILE *file, int *w, int *h); + /* ===================================== */ /* Internal APIs. Be careful with these. */ diff --git a/system/local/include/README.txt b/system/local/include/README.txt new file mode 100644 index 0000000000000..09bd03e060840 --- /dev/null +++ b/system/local/include/README.txt @@ -0,0 +1,2 @@ +You may place local includes that you require here and they will +be found by the default compiler search path. diff --git a/tests/cmake/target_library/CMakeLists.txt b/tests/cmake/target_library/CMakeLists.txt new file mode 100644 index 0000000000000..c70231922cdea --- /dev/null +++ b/tests/cmake/target_library/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 2.8) + +project(test_cmake) + +option(BUILD_SHARED_LIBS "Build with shared libraries." OFF) + +if (CMAKE_BUILD_TYPE STREQUAL Debug) + SET(linkFlags "-g4") +else() # Either MinSizeRel, RelWithDebInfo or Release, all which run with optimizations enabled. + SET(linkFlags "-O2") +endif() + +set(MAX_SRC_FILE_INDEX 30) +SET(TEST_SRC_FILE_BASE_NAME "this_is_a_test_src_file_with_a_quite_lengthy_name_to_simulate_very_long_command_line_length_problems_on_windows_") + + +foreach(i RANGE ${MAX_SRC_FILE_INDEX}) + set (TEST_FUNCTION_NAME "FooBar_${i}") + configure_file("srcfile.cmake" "${TEST_SRC_FILE_BASE_NAME}${i}.c") + configure_file("srcfile.cmake" "${TEST_SRC_FILE_BASE_NAME}${i}.cpp") + list(APPEND TEST_SOURCES "${TEST_SRC_FILE_BASE_NAME}${i}.c" "${TEST_SRC_FILE_BASE_NAME}${i}.cpp") +endforeach() + +add_library(test_cmake ${TEST_SOURCES}) + +if (WIN32) + message(FATAL_ERROR "WIN32 should not be defined when cross-compiling!") +endif() + +if (APPLE) + message(FATAL_ERROR "APPLE should not be defined when cross-compiling!") +endif() + +if (NOT EMSCRIPTEN) + message(FATAL_ERROR "EMSCRIPTEN should be defined when cross-compiling!") +endif() + +if (NOT CMAKE_C_SIZEOF_DATA_PTR) + message(FATAL_ERROR "CMAKE_C_SIZEOF_DATA_PTR was not defined!") +endif() + +# GOTCHA: If your project has custom link flags, these must be set *before* calling any of the em_link_xxx functions! +set_target_properties(test_cmake PROPERTIES LINK_FLAGS "${linkFlags}") diff --git a/tests/cmake/target_library/srcfile.cmake b/tests/cmake/target_library/srcfile.cmake new file mode 100644 index 0000000000000..10e9e6f8c1daf --- /dev/null +++ b/tests/cmake/target_library/srcfile.cmake @@ -0,0 +1,6 @@ +#include + +void @TEST_FUNCTION_NAME@() +{ + printf("@TEST_FUNCTION_NAME@"); +} diff --git a/tests/core/test_simd4.in b/tests/core/test_simd4.in new file mode 100644 index 0000000000000..b597d8a347749 --- /dev/null +++ b/tests/core/test_simd4.in @@ -0,0 +1,39 @@ +#include +#include + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_load_ps(const float *__p) +{ + return *(__m128*)__p; +} + +float simdAverage(float *src, int len) { + __m128 sumx4 = _mm_setzero_ps(); + for (int i = 0; i < len; i += 4) { + __m128 v = _mm_load_ps(src); + sumx4 = _mm_add_ps(sumx4, v); + src += 4; + } + float sumx4_mem[4]; + float *sumx4_ptr = sumx4_mem; + _mm_store_ps(sumx4_ptr, sumx4); + return (sumx4_mem[0] + sumx4_mem[1] + + sumx4_mem[2] + sumx4_mem[3])/len; +} + +void initArray(float *src, int len) { + for (int i = 0; i < len; ++i) { + src[i] = 0.1 * i; + } +} + +int main() { + const int len = 100000; + float src[len]; + float result = 0.0; + + initArray(src, len); + + result = simdAverage(src, len); + printf("averagex4 result: %.1f\n", result); +} \ No newline at end of file diff --git a/tests/core/test_simd4.out b/tests/core/test_simd4.out new file mode 100644 index 0000000000000..994497724de11 --- /dev/null +++ b/tests/core/test_simd4.out @@ -0,0 +1 @@ +averagex4 result: 4999.9 \ No newline at end of file diff --git a/tests/glfw.c b/tests/glfw.c index 79199d9a8b613..cbdc81fe500f3 100644 --- a/tests/glfw.c +++ b/tests/glfw.c @@ -376,6 +376,10 @@ void PullInfo(){ extension = "GL_EXT_framebuffer_object"; printf("'%s' extension is %s.\n", extension, glfwExtensionSupported(extension) ? "supported" : "not supported"); + extension = "glBindBuffer"; + void* proc_addr = glfwGetProcAddress(extension); + printf("'%s' extension proc address is %p.\n", extension, proc_addr); + printf("Sleeping 1 sec...\n"); glfwSleep(1); printf("...Done.\n"); diff --git a/tests/sdl_image.c b/tests/sdl_image.c index 523f89038ebaf..9639ea37f7bd9 100644 --- a/tests/sdl_image.c +++ b/tests/sdl_image.c @@ -4,6 +4,7 @@ #include #include #include +#include int testImage(SDL_Surface* screen, const char* fileName) { SDL_Surface *image = IMG_Load(fileName); @@ -18,7 +19,16 @@ int testImage(SDL_Surface* screen, const char* fileName) { int result = image->w; SDL_BlitSurface (image, NULL, screen, NULL); + + int w, h; + char *data = emscripten_get_preloaded_image_data(fileName, &w, &h); + + assert(data); + assert(w == image->w); + assert(h == image->h); + SDL_FreeSurface (image); + free(data); return result; } diff --git a/tests/test_core.py b/tests/test_core.py index bcb038300d560..b9057f4e9642d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -4892,6 +4892,15 @@ def test_simd3(self): self.do_run_from_file(src, output) + def test_simd4(self): + # test_simd4 is to test phi node handling of SIMD path + if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate + + test_path = path_from_root('tests', 'core', 'test_simd4') + src, output = (test_path + s for s in ('.in', '.out')) + + self.do_run_from_file(src, output) + def test_gcc_unmangler(self): if os.environ.get('EMCC_FAST_COMPILER') == '0': Settings.NAMED_GLOBALS = 1 # test coverage for this; fastcomp never names globals diff --git a/tests/test_other.py b/tests/test_other.py index 665832ee36fdc..39796b710c193 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -207,7 +207,8 @@ def test_emcc(self): (['-O2'], lambda generated: '// The Module object' not in generated, 'with opts, no comments in shell code'), (['-O2', '-g2'], lambda generated: '// The Module object' not in generated, 'with -g2, no comments in shell code'), (['-O2', '-g3'], lambda generated: '// The Module object' in generated, 'with -g3, yes comments in shell code'), - (['-O2', '-profiling'], lambda generated: '// The Module object' in generated, 'with -profiling, yes comments in shell code'), + (['-O2', '-profiling'], lambda generated: '// The Module object' in generated or os.environ.get('EMCC_FAST_COMPILER') == '0', 'with -profiling, yes comments in shell code (in fastcomp)'), + ]: print params, text self.clear() @@ -354,9 +355,10 @@ def check_makefile(configuration, dirname): except KeyError: postbuild = None - cmake_cases = ['target_js', 'target_html'] - cmake_outputs = ['test_cmake.js', 'hello_world_gles.html'] - for i in range(0, 2): + cmake_cases = ['target_js', 'target_html', 'target_library', 'target_library'] + cmake_outputs = ['test_cmake.js', 'hello_world_gles.html', 'libtest_cmake.a', 'libtest_cmake.so'] + cmake_arguments = ['', '', '-DBUILD_SHARED_LIBS=OFF', '-DBUILD_SHARED_LIBS=ON'] + for i in range(0, len(cmake_cases)): for configuration in ['Debug', 'Release']: # CMake can be invoked in two ways, using 'emconfigure cmake', or by directly running 'cmake'. # Test both methods. @@ -374,11 +376,11 @@ def check_makefile(configuration, dirname): if invoke_method == 'cmake': # Test invoking cmake directly. cmd = ['cmake', '-DCMAKE_TOOLCHAIN_FILE='+path_from_root('cmake', 'Platform', 'Emscripten.cmake'), - '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir] + '-DCMAKE_BUILD_TYPE=' + configuration, cmake_arguments[i], '-G', generator, cmakelistsdir] else: # Test invoking via 'emconfigure cmake' - cmd = [emconfigure, 'cmake', '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir] - + cmd = [emconfigure, 'cmake', '-DCMAKE_BUILD_TYPE=' + configuration, cmake_arguments[i], '-G', generator, cmakelistsdir] + ret = Popen(cmd, stdout=None if verbose_level >= 2 else PIPE, stderr=None if verbose_level >= 1 else PIPE).communicate() if len(ret) > 1 and ret[1] != None and len(ret[1].strip()) > 0: logging.error(ret[1]) # If there were any errors, print them directly to console for diagnostics. diff --git a/tools/ffdb.py b/tools/ffdb.py index c22fd9dbd9930..70b93feecdcf8 100755 --- a/tools/ffdb.py +++ b/tools/ffdb.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess +import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess, re, base64, struct, imghdr +ADB = 'adb' # Path to the adb executable LOG_VERBOSE = False # Verbose printing enabled with --verbose HOST = 'localhost' # The remote host to connect to the B2G device PORT = 6000 # The port on the host on which the B2G device listens on @@ -18,7 +19,13 @@ def sizeof_fmt(num): return "%3.1f%s" % (num, 'TB') def zipdir(path, zipfilename): - zipf = zipfile.ZipFile(zipfilename, 'w') + try: + import zlib + zip_mode = zipfile.ZIP_DEFLATED + except: + zip_mode = zipfile.ZIP_STORED + + zipf = zipfile.ZipFile(zipfilename, 'w', zip_mode) files_to_compress = [] for root, dirs, files in os.walk(path): for file in files: @@ -29,9 +36,10 @@ def zipdir(path, zipfilename): (root, file) = tuple filename = os.path.join(root, file) filesize = os.path.getsize(filename) - print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + os.path.relpath(filename, path) + '" (' + sizeof_fmt(filesize) + ')...' + path_in_archive = os.path.relpath(filename, path) + print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + path_in_archive + '" (' + sizeof_fmt(filesize) + ')...' n += 1 - zipf.write(os.path.join(root, file)) + zipf.write(os.path.join(root, file), path_in_archive) zipf.close() print 'Done. ' @@ -46,16 +54,27 @@ def format_html(msg): # Prints a verbose log message to stdout channel. Only shown if run with --verbose. def logv(msg): if LOG_VERBOSE: - sys.stdout.write(format_html(msg)) + sys.stdout.write(format_html(msg) + '\n') sys.stdout.flush() # Reads data from the socket, and tries to parse what we have got so far as a JSON message. # The messages are of form "bytelength:{jsondict}", where bytelength tells how many bytes # there are in the data that comes after the colon. # Returns a JSON dictionary of the received message. -def read_b2g_response(): +def read_b2g_response(print_errors_to_console = True): global read_queue, b2g_socket - read_queue += b2g_socket.recv(65536*2) + try: + read_queue += b2g_socket.recv(65536*2) + except KeyboardInterrupt: + print ' Aborted by user' + sys.exit(1) + except Exception, e: + if e[0] == 57: # Socket is not connected + print 'Error! Failed to receive data from the device: socket is not connected!' + sys.exit(1) + else: + raise + payload = '' while ':' in read_queue: semicolon = read_queue.index(':') payload_len = int(read_queue[:semicolon]) @@ -66,10 +85,13 @@ def read_b2g_response(): read_queue = read_queue[semicolon+1+payload_len:] logv('Read a message of size ' + str(payload_len) + 'b from socket.') payload = json.loads(payload) + # Log received errors immediately to console + if print_errors_to_console and 'error' in payload: + print >> sys.stderr, 'Received error "' + payload['error'] + '"! Reason: ' + payload['message'] return payload # Sends a command to the B2G device and waits for the response and returns it as a JSON dict. -def send_b2g_cmd(to, cmd, data = {}): +def send_b2g_cmd(to, cmd, data = {}, print_errors_to_console = True): global b2g_socket msg = { 'to': to, 'type': cmd} msg = dict(msg.items() + data.items()) @@ -78,7 +100,7 @@ def send_b2g_cmd(to, cmd, data = {}): msg = str(len(msg))+':'+msg logv('Sending cmd:' + cmd + ' to:' + to) b2g_socket.sendall(msg) - return read_b2g_response() + return read_b2g_response(print_errors_to_console) def escape_bytes(b): return str(b) @@ -102,7 +124,16 @@ def send_b2g_data_chunk(to, data_blob): i += 1 message = '{"to":"'+to+'","type":"chunk","chunk":"' + ''.join(byte_str) + '"}' message = str(len(message)) + ':' + message + logv('{"to":"'+to+'","type":"chunk","chunk":""}') + b2g_socket.sendall(message) + return read_b2g_response() + +def send_b2g_bulk_data(to, data_blob): + message = 'bulk ' + to + ' stream ' + str(len(data_blob)) + ':' + logv(message) b2g_socket.sendall(message) + b2g_socket.sendall(data_blob) + # It seems that B2G doesn't send any response JSON back after a bulk transfer is finished, so no read_b2g_response() here. # Queries the device for a list of all installed apps. def b2g_get_appslist(): @@ -130,8 +161,190 @@ def print_applist(applist, running_app_manifests, print_removable): num_printed += 1 return num_printed +def adb_devices(): + try: + devices = subprocess.check_output([ADB, 'devices']) + devices = devices.strip().split('\n')[1:] + devices = map(lambda x: x.strip().split('\t'), devices) + return devices + except Exception, e: + return [] + +def b2g_get_prefs_filename(): + return subprocess.check_output([ADB, 'shell', 'echo', '-n', '/data/b2g/mozilla/*.default/prefs.js']) + +def b2g_get_prefs_data(): + return subprocess.check_output([ADB, 'shell', 'cat', '/data/b2g/mozilla/*.default/prefs.js']) + +def b2g_get_pref(sub): + prefs_data = b2g_get_prefs_data().split('\n') + # Filter to find all prefs that have the substring 'sub' in them. + r = re.compile('user_pref\w*\(\w*"([^"]*)"\w*,\w*([^\)]*)') + for line in prefs_data: + m = r.match(line) + if m and (sub is None or sub in m.group(1)): + print m.group(1) + ': ' + m.group(2).strip() + +def b2g_set_pref(pref, value): + prefs_data = b2g_get_prefs_data().split('\n') + # Remove any old value of this pref. + r = re.compile('user_pref\w*\(\w*"([^"]*)"\w*,\w*([^\)]*)') + new_prefs_data = [] + for line in prefs_data: + m = r.match(line) + if not m or m.group(1) != pref: + new_prefs_data += [line] + + if value != None: + print 'Setting pref "' + pref + '" = ' + value + new_prefs_data += ['user_pref("' + pref + '", ' + value + ');'] + else: + print 'Unsetting pref "' + pref + '"' + (oshandle, tempfilename) = tempfile.mkstemp(suffix='.js', prefix='ffdb_temp_') + os.write(oshandle, '\n'.join(new_prefs_data)); + + # Write the new pref + subprocess.check_output([ADB, 'shell', 'stop', 'b2g']) + subprocess.check_output([ADB, 'push', tempfilename, b2g_get_prefs_filename()]) + subprocess.check_output([ADB, 'shell', 'start', 'b2g']) + print 'Rebooting phone...' + + def delete_temp_file(): + os.remove(tempfilename) + atexit.register(delete_temp_file) + +def get_packaged_app_manifest(target_app_path): + if os.path.isdir(target_app_path): + manifest_file = os.path.join(target_app_path, 'manifest.webapp') + if not os.path.isfile(manifest_file): + print "Error: Failed to find FFOS packaged app manifest file '" + manifest_file + "'! That directory does not contain a packaged app?" + sys.exit(1) + return json.loads(open(manifest_file, 'r').read()) + elif target_app_path.endswith('.zip') and os.path.isfile(target_app_path): + try: + z = zipfile.ZipFile(target_app_path, "r") + bytes = z.read('manifest.webapp') + except Exception, e: + print "Error: Failed to read FFOS packaged app manifest file 'manifest.webapp' in zip file '" + target_app_path + "'! Error: " + str(e) + sys.exit(1) + return None + return json.loads(str(bytes)) + else: + print "Error: Path '" + target_app_path + "' is neither a directory or a .zip file to represent the location of a FFOS packaged app!" + sys.exit(1) + return None + +def b2g_install(target_app_path): + if os.path.isdir(target_app_path): + print 'Zipping up the contents of directory "' + target_app_path + '"...' + (oshandle, tempzip) = tempfile.mkstemp(suffix='.zip', prefix='ffdb_temp_') + zipdir(target_app_path, tempzip) + target_app_path = tempzip + # Remember to delete the temporary package after we quit. + def delete_temp_file(): + os.remove(tempzip) + atexit.register(delete_temp_file) + + print 'Uploading application package "' + target_app_path + '"...' + print 'Size of compressed package: ' + sizeof_fmt(os.path.getsize(target_app_path)) + '.' + app_file = open(target_app_path, 'rb') + data = app_file.read() + file_size = len(data) + + uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage', { 'bulk': 'true'}, print_errors_to_console = False) # This may fail if on old device. + start_time = time.time() + if 'actor' in uploadResponse and 'BulkActor' in uploadResponse['actor']: # New B2G 2.0 hotness: binary data transfer + packageUploadActor = uploadResponse['actor'] + send_b2g_bulk_data(packageUploadActor, data) + else: # Old B2G 1.4 and older, serialize binary data in JSON text strings (SLOW!) + print 'Bulk upload is not supported, uploading binary data with old slow format. Consider flashing your device to FFOS 2.0 or newer to enjoy faster upload speeds.' + uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage') + packageUploadActor = uploadResponse['actor'] + chunk_size = 4*1024*1024 + i = 0 + while i < file_size: + chunk = data[i:i+chunk_size] + + send_b2g_data_chunk(packageUploadActor, chunk) + i += chunk_size + bytes_uploaded = min(i, file_size) + cur_time = time.time() + secs_elapsed = cur_time - start_time + percentage_done = bytes_uploaded * 1.0 / file_size + total_time = secs_elapsed / percentage_done + time_left = total_time - secs_elapsed + print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed) + + app_local_id = str(uuid.uuid4()) + reply = send_b2g_cmd(webappsActorName, 'install', { 'appId': app_local_id, 'upload': packageUploadActor }) + cur_time = time.time() + secs_elapsed = cur_time - start_time + print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed) + return reply['appId'] + +def b2g_app_command(app_command, app_name): + apps = b2g_get_appslist() + for app in apps: + if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name: + send_b2g_cmd(webappsActorName, app_command, { 'manifestURL': app['manifestURL'] }) + return 0 + print 'Error! Application "' + app_name + '" was not found! Use the \'list\' command to find installed applications.' + return 1 + +def b2g_log(app_name, clear=False): + apps = b2g_get_appslist() + appActor = '' + for app in apps: + if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name: + appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] }) + break + if 'actor' in appActor: + consoleActor = appActor['actor']['consoleActor'] + + if clear: + send_b2g_cmd(consoleActor, 'clearMessagesCache') + print 'Cleared message log.' + sys.exit(0) + + msgs = send_b2g_cmd(consoleActor, 'startListeners', { 'listeners': ['PageError','ConsoleAPI','NetworkActivity','FileActivity'] }) + + def log_b2g_message(msg): + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = "\033[1m" + msgs = [] + if 'type' in msg and msg['type'] == 'consoleAPICall': + msgs = [msg['message']] + elif 'messages' in msg: + msgs = msg['messages'] + + for m in msgs: + args = m['arguments'] + + for arg in args: + if m['level'] == 'log': + color = 'I/' + elif m['level'] == 'warn': + color = WARNING + 'W/' + elif m['level'] == 'error': + color = FAIL + 'E/' + else: + color = m['level'] + '/' + + print color + str(m['functionName']) + '@' + str(m['filename']) + ':' + str(m['lineNumber']) + ': ' + str(arg) + ENDC + + msgs = send_b2g_cmd(consoleActor, 'getCachedMessages', { 'messageTypes': ['PageError', 'ConsoleAPI'] }) + log_b2g_message(msgs) + + while True: + msg = read_b2g_response() + log_b2g_message(msg) + else: + print 'Application "' + sys.argv[2] + '" is not running!' + def main(): - global b2g_socket, webappsActorName + global b2g_socket, webappsActorName, HOST, PORT, VERBOSE, ADB if len(sys.argv) < 2 or '--help' in sys.argv or 'help' in sys.argv or '-v' in sys.argv: print '''Firefox OS Debug Bridge, a tool for automating FFOS device tasks from the command line. @@ -140,30 +353,85 @@ def main(): list [--running] [--all]: Prints out the user applications installed on the device. If --running is passed, only the currently opened apps are shown. If --all is specified, then also uninstallable system applications are listed. - launch : Starts the given application. If already running, brings to front. + launch [--log]: Starts the given application. If already running, brings to front. If the --log option is passed, ffdb will + start persistently logging the execution of the given application. close : Terminates the execution of the given application. uninstall : Removes the given application from the device. - install : Uploads and installs a packaged app that resides in the given local directory. + install [--run] [--log]: Uploads and installs a packaged app that resides in the given local directory. may either refer to a directory containing a packaged app, or to a prepackaged zip file. + If the --run option is passed, the given application is immediately launched after the installation finishes. + If the --log option is passed, ffdb will start persistently logging the execution of the installed application. log [--clear]: Starts a persistent log listener that reads web console messages from the given application. If --clear is passed, the message log for that application is cleared instead. navigate : Opens the given web page in the B2G browser. + screenshot [filename.png]: Takes a screenshot of the current contents displayed on the device. If an optional + filename is specified, the screenshot is saved to that file. Otherwise the filename + will be autogenerated. + get : Fetches the value of the given developer pref option from the FFOS device and prints it to console. + set : Writes the given pref option to the FFOS device and restarts the B2G process on it for the change to take effect. + unset : Removes the given pref option from the FFOS device and restarts the B2G process on it for the change to take effect. + + hide-prompt: Permanently removes the remote debugging connection dialog from showing up, and reboots the phone. This command is + provided for conveniency, and is the same as calling './ffdb.py set devtools.debugger.prompt-connection false' + restore-prompt: Restores the remote debugging connection dialog prompt to its default state. + + Options: Additionally, the following options may be passed to control FFDB execution: + + --host : Specifies the target network address to connect to. Default: 'localhost'. + --port : Specifies the network port to connect to. Default: 6000. + --verbose: Enables verbose printing, mostly useful for debugging. + --simulator: Signal that we will be connecting to a FFOS simulator and not a real device. In the above, whenever a command requires an to be specified, either the human-readable name, localId or manifestURL of the application can be used.''' sys.exit(0) + connect_to_simulator = False + + options_with_value = ['--host', '--port'] + options = options_with_value + ['--verbose', '--simulator'] + # Process options + for i in range(0, len(sys.argv)): + if sys.argv[i] in options_with_value: + if i+1 >= sys.argv or sys.argv[i+1].startswith('-'): + print >> sys.stderr, "Missing value for option " + sys.argv[i] +'!' + sys.exit(1) + if sys.argv[i] == '--host': + HOST = sys.argv[i+1] + elif sys.argv[i] == '--port': + PORT = int(sys.argv[i+1]) + elif sys.argv[i] == '--verbose': + VERBOSE = True + elif sys.argv[i] == '--simulator': + connect_to_simulator = True + + # Clear the processed options so that parsing the commands below won't trip up on these. + if sys.argv[i] in options: sys.argv[i] = '' + if sys.argv[i] in options_with_value: sys.argv[i+1] = '' + + sys.argv = filter(lambda x: len(x) > 0, sys.argv) + + # Double-check that the device is found via adb: + if (HOST == 'localhost' or HOST == '127.0.0.1') and not connect_to_simulator: + devices = adb_devices() + if len(devices) == 0: + print 'Error! Failed to connect to B2G device debugger socket at address ' + HOST + ':' + str(PORT) + ' and no devices were detected via adb. Please double-check the following and try again: ' + print ' 1) The device is powered on and connected to the computer with an USB cable.' + print ' 2) ADB and DevTools debugging is enabled on the device. (Settings -> Developer -> Debugging via USB: "ADB and DevTools"' + print ' 3) The device is listed when you run "adb devices" on the command line.' + print ' 4) When launching ffdb, remember to acknowledge the "incoming debug connection" dialog if it pops up on the device.' + sys.exit(1) b2g_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: b2g_socket.connect((HOST, PORT)) except Exception, e: if e[0] == 61: # Connection refused - if HOST == 'localhost' or HOST == '127.0.0.1': - cmd = ['adb', 'forward', 'tcp:'+str(PORT), 'localfilesystem:/data/local/debugger-socket'] + if (HOST == 'localhost' or HOST == '127.0.0.1') and not connect_to_simulator: + cmd = [ADB, 'forward', 'tcp:'+str(PORT), 'localfilesystem:/data/local/debugger-socket'] print 'Connection to ' + HOST + ':' + str(PORT) + ' refused, attempting to forward device debugger-socket to local address by calling ' + str(cmd) + ':' else: - print 'Error! Failed to connect to B2G device debugger socket at address ' + HOST + ':' + str(PORT) + '!' + print 'Error! Failed to connect to B2G ' + ('simulator' if connect_to_simulator else 'device') + ' debugger socket at address ' + HOST + ':' + str(PORT) + '!' sys.exit(1) try: retcode = subprocess.check_call(cmd) @@ -187,6 +455,9 @@ def main(): logv('Connected. Handshake: ' + str(handshake)) data = send_b2g_cmd('root', 'listTabs') + if not 'deviceActor' in data: + print 'Error! Debugging connection was not available. Make sure that the "Remote debugging" developer option on the device is set to "ADB and Devtools".' + sys.exit(1) deviceActorName = data['deviceActor'] logv('deviceActor: ' + deviceActorName) webappsActorName = data['webappsActor'] @@ -195,9 +466,8 @@ def main(): send_b2g_cmd(deviceActorName, 'getDescription') send_b2g_cmd(deviceActorName, 'getRawPermissionsTable') - apps = b2g_get_appslist() - if sys.argv[1] == 'list': + apps = b2g_get_appslist() running_app_manifests = b2g_get_runningapps() printed_apps = apps print_only_running = '--running' in sys.argv and not '--all' in sys.argv @@ -218,54 +488,27 @@ def main(): if len(sys.argv) < 3: print 'Error! No application name given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' ' return 1 - for app in apps: - if str(app['localId']) == sys.argv[2] or app['name'] == sys.argv[2] or app['manifestURL'] == sys.argv[2]: - send_b2g_cmd(webappsActorName, sys.argv[1], { 'manifestURL': app['manifestURL'] }) - return 0 - print 'Error! Application "' + sys.argv[2] + '" was not found! Use the \'list\' command to find installed applications.' - return 1 + ret = b2g_app_command(sys.argv[1], sys.argv[2]) + if ret == 0 and '--log' in sys.argv: + b2g_log(sys.argv[2]) elif sys.argv[1] == 'install': if len(sys.argv) < 3: print 'Error! No application path given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' ' return 1 target_app_path = sys.argv[2] - if os.path.isdir(target_app_path): - print 'Zipping up the contents of directory "' + target_app_path + '"...' - (oshandle, tempzip) = tempfile.mkstemp(suffix='.zip', prefix='ffdb_temp_') - zipdir(target_app_path, tempzip) - target_app_path = tempzip - # Remember to delete the temporary package after we quit. - def delete_temp_file(): - os.remove(tempzip) - atexit.register(delete_temp_file) - - print 'Uploading application package "' + target_app_path + '"...' - print 'Size of compressed package: ' + sizeof_fmt(os.path.getsize(target_app_path)) + '.' - uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage') - packageUploadActor = uploadResponse['actor'] - app_file = open(target_app_path, 'rb') - data = app_file.read() - file_size = len(data) - chunk_size = 4*1024*1024 - i = 0 - start_time = time.time() - while i < file_size: - chunk = data[i:i+chunk_size] - - send_b2g_data_chunk(packageUploadActor, chunk) - i += chunk_size - bytes_uploaded = min(i, file_size) - cur_time = time.time() - secs_elapsed = cur_time - start_time - percentage_done = bytes_uploaded * 1.0 / file_size - total_time = secs_elapsed / percentage_done - time_left = total_time - secs_elapsed - print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed) - send_b2g_cmd(webappsActorName, 'install', { 'appId': str(uuid.uuid4()), 'upload': packageUploadActor }) - - cur_time = time.time() - secs_elapsed = cur_time - start_time - print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed) + # Kill and uninstall old running app execution before starting. + if '--run' in sys.argv: + app_manifest = get_packaged_app_manifest(target_app_path) + b2g_app_command('close', app_manifest['name']) + b2g_app_command('uninstall', app_manifest['name']) + # Upload package + app_id = b2g_install(target_app_path) + # Launch it immediately if requested. + if '--run' in sys.argv: + b2g_app_command('launch', app_id) + # Don't quit, but keep logging the app if requested. + if '--log' in sys.argv: + b2g_log(app_id) elif sys.argv[1] == 'navigate': if len(sys.argv) < 3: print 'Error! No URL given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' ' @@ -281,55 +524,79 @@ def delete_temp_file(): else: print 'Web browser is not running!' elif sys.argv[1] == 'log': - appActor = '' - for app in apps: - if str(app['localId']) == sys.argv[2] or app['name'] == sys.argv[2] or app['manifestURL'] == sys.argv[2]: - appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] }) - break - if 'actor' in appActor: - consoleActor = appActor['actor']['consoleActor'] - - if '-c' in sys.argv or '-clear' in sys.argv or '--clear' in sys.argv: - send_b2g_cmd(consoleActor, 'clearMessagesCache') - print 'Cleared message log.' - sys.exit(0) - - msgs = send_b2g_cmd(consoleActor, 'startListeners', { 'listeners': ['PageError','ConsoleAPI','NetworkActivity','FileActivity'] }) - - def log_b2g_message(msg): - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = "\033[1m" - msgs = [] - if 'type' in msg and msg['type'] == 'consoleAPICall': - msgs = [msg['message']] - elif 'messages' in msg: - msgs = msg['messages'] - - for m in msgs: - args = m['arguments'] - - for arg in args: - if m['level'] == 'log': - color = 'I/' - elif m['level'] == 'warn': - color = WARNING + 'W/' - elif m['level'] == 'error': - color = FAIL + 'E/' - else: - color = m['level'] + '/' - - print color + str(m['functionName']) + '@' + str(m['filename']) + ':' + str(m['lineNumber']) + ': ' + str(arg) + ENDC - - msgs = send_b2g_cmd(consoleActor, 'getCachedMessages', { 'messageTypes': ['PageError', 'ConsoleAPI'] }) - log_b2g_message(msgs) - - while True: - msg = read_b2g_response() - log_b2g_message(msg) + clear = '-c' in sys.argv or '-clear' in sys.argv or '--clear' in sys.argv + b2g_log(sys.argv[2], clear) + elif sys.argv[1] == 'screenshot': + if len(sys.argv) >= 3: + filename = sys.argv[2] + if not filename.endswith('.png'): + print >> sys.stderr, "Writing screenshots only to .png files are supported!" + sys.exit(1) else: - print 'Application "' + sys.argv[2] + '" is not running!' + filename = time.strftime("screen_%Y%m%d_%H%M%S.png", time.gmtime()) + + data_reply = send_b2g_cmd(deviceActorName, 'screenshotToDataURL') + data = data_reply['value'] + data_get_actor = data['actor'] + data_len = int(data['length']) + data_str = data['initial'] + delim = re.search(",", data_str).start() + data_format = data_str[:delim] + if data_format != "data:image/png;base64": + print >> sys.stderr, "Error: Received screenshot from device in an unexpected format '" + data_format + "'!" + sys.exit(1) + data = data_str[delim+1:] + chunk_size = 65000 + pos = len(data_str) + while pos < data_len: + bytes_to_read = min(data_len - pos, chunk_size) + data_reply = send_b2g_cmd(data_get_actor, 'substring', { 'start': str(pos), 'end': str(pos + bytes_to_read) }) + if len(data_reply['substring']) != bytes_to_read: + print >> sys.stderr, 'Error! Expected to receive ' + str(bytes_to_read) + ' bytes of image data, but got ' + str(len(data_reply['substring'])) + ' bytes instead!' + sys.exit(1) + data += data_reply['substring'] + pos += bytes_to_read + send_b2g_cmd(data_get_actor, 'release') # We need to explicitly free the screenshot image string from the device, or the Devtools connection leaks resources! + binary_data = base64.b64decode(data) + open(filename, 'wb').write(binary_data) + + def get_png_image_size(filename): + fhandle = open(filename, 'rb') + head = fhandle.read(24) + if len(head) != 24: + return (-1, -1) + check = struct.unpack('>i', head[4:8])[0] + if check != 0x0d0a1a0a: + return (-1, -1) + return struct.unpack('>ii', head[16:24]) + + width, height = get_png_image_size(filename) + if width <= 0 or height <= 0: + print >> sys.stderr, "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "', but the contents may be corrupted!" + else: + print "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "' (" + str(width) + 'x' + str(height) + ' pixels).' + elif sys.argv[1] == 'get': + b2g_get_pref(sys.argv[2] if len(sys.argv) >= 3 else None) + elif sys.argv[1] == 'set': + if len(sys.argv) < 3: + print 'Error! No pref name to set given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' ' + sys.exit(1) + if len(sys.argv) < 4: + print 'Error! No value given to set! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' ' + sys.exit(1) + if len(sys.argv) > 4: + print 'Error! Too many arguments given (' + str(sys.argv) + '), need exactly four! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' ' + sys.exit(1) + b2g_set_pref(sys.argv[2], sys.argv[3]) + elif sys.argv[1] == 'unset': + if len(sys.argv) < 3: + print 'Error! No pref name given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' ' + sys.exit(1) + b2g_set_pref(sys.argv[2], None) + elif sys.argv[1] == 'hide-prompt': + b2g_set_pref('devtools.debugger.prompt-connection', 'false') + elif sys.argv[1] == 'restore-prompt': + b2g_set_pref('devtools.debugger.prompt-connection', None) else: print "Unknown command '" + sys.argv[1] + "'! Pass --help for instructions." diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 989a79322a52d..9b8387be91985 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2087,12 +2087,18 @@ function registerizeHarder(ast) { function pushActiveLabels(onContinue, onBreak) { // Push the target junctions for continuing/breaking a loop. // This should be called before traversing into a loop. - var newLabels = copy(activeLabels[activeLabels.length-1]); + var prevLabels = activeLabels[activeLabels.length-1]; + var newLabels = copy(prevLabels); newLabels[null] = [onContinue, onBreak]; if (nextLoopLabel) { newLabels[nextLoopLabel] = [onContinue, onBreak]; nextLoopLabel = null; } + // An unlabelled 'continue' should jump to innermost loop, + // ignoring any nested 'switch' statements. + if (onContinue === null && prevLabels[null]) { + newLabels[null][0] = prevLabels[null][0]; + } activeLabels.push(newLabels); } @@ -5177,8 +5183,11 @@ function fixDotZero(js) { } function asmLastOpts(ast) { + var statsStack = []; traverseGeneratedFunctions(ast, function(fun) { traverse(fun, function(node, type) { + var stats = getStatements(node); + if (stats) statsStack.push(stats); if (type === 'while' && node[1][0] === 'num' && node[1][1] === 1 && node[2][0] === 'block' && node[2].length == 2) { // This is at the end of the pipeline, we can assume all other optimizations are done, and we modify loops // into shapes that might confuse other passes @@ -5186,15 +5195,28 @@ function asmLastOpts(ast) { // while (1) { .. if (..) { break } } ==> do { .. } while(..) var stats = node[2][1]; var last = stats[stats.length-1]; - if (last && last[0] === 'if' && !last[3] && last[2][0] === 'block' && last[2][1][0] && last[2][1][0][0] === 'break' && !last[2][1][0][1]) { + if (last && last[0] === 'if' && !last[3] && last[2][0] === 'block' && last[2][1][0]) { + var lastStats = last[2][1]; + var lastNum = lastStats.length; + var lastLast = lastStats[lastNum-1]; + if (!(lastLast[0] === 'break' && !lastLast[1])) return;// if not a simple break, dangerous + for (var i = 0; i < lastNum; i++) { + if (lastStats[i][0] !== 'stat' && lastStats[i][0] !== 'break') return; // something dangerous + } + // ok, a bunch of statements ending in a break var abort = false; var stack = 0; + var breaks = 0; traverse(stats, function(node, type) { - if (type == 'continue') { - if (stack == 0 || node[1]) { // abort if labeled (we do not analyze labels here yet), or a continue directly on us + if (type === 'continue') { + if (stack === 0 || node[1]) { // abort if labeled (we do not analyze labels here yet), or a continue directly on us abort = true; return true; } + } else if (type === 'break') { + if (stack === 0 || node[1]) { // relevant if labeled (we do not analyze labels here yet), or a break directly on us + breaks++; + } } else if (type in LOOP) { stack++; } @@ -5204,6 +5226,15 @@ function asmLastOpts(ast) { } }); if (abort) return; + assert(breaks > 0); + if (lastStats.length > 1 && breaks !== 1) return; // if we have code aside from the break, we can only move it out if there is just one break + // start to optimize + if (lastStats.length > 1) { + var parent = statsStack[statsStack.length-1]; + var me = parent.indexOf(node); + if (me < 0) return; // not always directly on a stats, could be in a label for example + parent.splice.apply(parent, [me+1, 0].concat(lastStats.slice(0, lastStats.length-1))); + } var conditionToBreak = last[1]; stats.pop(); node[0] = 'do'; @@ -5232,6 +5263,9 @@ function asmLastOpts(ast) { } } } + }, function(node, type) { + var stats = getStatements(node); + if (stats) statsStack.pop(); }); }); } diff --git a/tools/shared.py b/tools/shared.py index 7aaa4136b7d7d..eda583045d2f5 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -706,10 +706,8 @@ def get_llvm_target(): path_from_root('system', 'include', 'compat'), path_from_root('system', 'include'), path_from_root('system', 'include', 'emscripten'), - path_from_root('system', 'include', 'bsd'), # posix stuff path_from_root('system', 'include', 'libc'), path_from_root('system', 'include', 'gfx'), - path_from_root('system', 'include', 'net'), path_from_root('system', 'include', 'SDL'), ] @@ -1426,12 +1424,17 @@ def emscripten(filename, append_ext=True, extra_args=[]): settings = Settings.serialize() args = settings + extra_args if WINDOWS: - args = ['@' + response_file.create_response_file(args, TEMP_DIR)] + rsp_file = response_file.create_response_file(args, TEMP_DIR) + args = ['@' + rsp_file] cmdline = [PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + args if jsrun.TRACK_PROCESS_SPAWNS: logging.info('Executing emscripten.py compiler with cmdline "' + ' '.join(cmdline) + '"') jsrun.timeout_run(Popen(cmdline, stdout=PIPE), None, 'Compiling') + # Clean up .rsp file the compiler used after we are finished. + if WINDOWS: + try_delete(rsp_file) + # Detect compilation crashes and errors assert os.path.exists(filename + '.o.js'), 'Emscripten failed to generate .js' diff --git a/tools/system_libs.py b/tools/system_libs.py index 78bf2d48cd116..f7a3e1ac03941 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -427,12 +427,18 @@ def create_gl(): # TODO: Move all __deps from src/library*.js to deps_info.json, and use that single source of info # both here and in the JS compiler. deps_info = json.loads(open(shared.path_from_root('src', 'deps_info.json')).read()) + added = set() def add_back_deps(need): + more = False for ident, deps in deps_info.iteritems(): - if ident in need.undefs: + if ident in need.undefs and not ident in added: + added.add(ident) + more = True for dep in deps: need.undefs.add(dep) shared.Settings.EXPORTED_FUNCTIONS.append('_' + dep) + if more: + add_back_deps(need) # recurse to get deps of deps for symbols in symbolses: add_back_deps(symbols) diff --git a/tools/test-js-optimizer-asm-last-output.js b/tools/test-js-optimizer-asm-last-output.js index 5e3685e2781ae..95afaeb7a0b29 100644 --- a/tools/test-js-optimizer-asm-last-output.js +++ b/tools/test-js-optimizer-asm-last-output.js @@ -72,5 +72,31 @@ function looop() { break; } } + do { + blah(); + } while (!shah()); + a = b; + LABELED : while (1) { + blah(); + if (shah()) { + c = d; + break; + } + } + while (1) { + blah(); + if (check) break; + if (shah()) { + e = f; + break; + } + } + do { + blah(); + while (1) { + if (check) break; + } + } while (!shah()); + g = h; } diff --git a/tools/test-js-optimizer-asm-last.js b/tools/test-js-optimizer-asm-last.js index d8caeb418b73a..6261e9158ad3e 100644 --- a/tools/test-js-optimizer-asm-last.js +++ b/tools/test-js-optimizer-asm-last.js @@ -87,6 +87,38 @@ function looop() { break; } } + while (1) { + blah(); + if (shah()) { + a = b; + break; + } + } + LABELED: while (1) { + blah(); + if (shah()) { + c = d; + break; + } + } + while (1) { + blah(); + if (check) break; // prevents optimization + if (shah()) { + e = f; + break; + } + } + while (1) { + blah(); + while (1) { + if (check) break; // safe to optimize + } + if (shah()) { + g = h; + break; + } + } } // EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall", "looop"]