diff options
Diffstat (limited to 'src/runtime.js')
-rw-r--r-- | src/runtime.js | 184 |
1 files changed, 126 insertions, 58 deletions
diff --git a/src/runtime.js b/src/runtime.js index 9bedfe68..a9265e70 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -25,26 +25,23 @@ var RuntimeGenerator = { sep = sep || ';'; var ret = RuntimeGenerator.alloc(size, 'STACK', false, sep, USE_TYPED_ARRAYS != 2 || (isNumber(size) && parseInt(size) % {{{ STACK_ALIGN }}} == 0)); if (ASSERTIONS) { - ret += sep + 'assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')'; + ret += sep + '(assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)'; } return ret; }, stackEnter: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; - var ret = 'var __stackBase__ = ' + (ASM_JS ? '0; __stackBase__ = ' : '') + 'STACKTOP'; - if (initial > 0) ret += '; STACKTOP = (STACKTOP + ' + initial + ')|0'; + var ret = 'var sp=' + (ASM_JS ? '0;sp=' : '') + 'STACKTOP'; + if (initial > 0) ret += ';STACKTOP=(STACKTOP+' + initial + ')|0'; if (USE_TYPED_ARRAYS == 2) { assert(initial % Runtime.STACK_ALIGN == 0); if (ASSERTIONS && Runtime.STACK_ALIGN == 4) { - ret += '; assert(' + asmCoercion('!(STACKTOP&3)', 'i32') + ')'; + ret += '; (assert(' + asmCoercion('!(STACKTOP&3)', 'i32') + ')|0)'; } } if (ASSERTIONS) { - ret += '; assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')'; - } - if (false) { - ret += '; _memset(' + asmCoercion('__stackBase__', 'i32') + ', 0, ' + initial + ')'; + ret += '; (assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)'; } return ret; }, @@ -52,10 +49,10 @@ var RuntimeGenerator = { stackExit: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; var ret = ''; - if (SAFE_HEAP) { - ret += 'var i = __stackBase__; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }'; + if (SAFE_HEAP && !ASM_JS) { + ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }'; } - return ret += 'STACKTOP = __stackBase__'; + return ret += 'STACKTOP=sp'; }, // An allocation that cannot normally be free'd (except through sbrk, which once @@ -85,8 +82,8 @@ var RuntimeGenerator = { // Rounding is inevitable if the number is large. This is a particular problem for small negative numbers // (-1 will be rounded!), so handle negatives separately and carefully makeBigInt: function(low, high, unsigned) { - var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))'; - var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))'; + var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))'; + var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))'; if (typeof unsigned === 'string') return '(' + unsigned + ' ? ' + unsignedRet + ' : ' + signedRet + ')'; return unsigned ? unsignedRet : signedRet; } @@ -112,8 +109,7 @@ var Runtime = { if (isNumber(target) && isNumber(quantum)) { return Math.ceil(target/quantum)*quantum; } else if (isNumber(quantum) && isPowerOfTwo(quantum)) { - var logg = log2(quantum); - return '((((' +target + ')+' + (quantum-1) + ')>>' + logg + ')<<' + logg + ')'; + return '(((' +target + ')+' + (quantum-1) + ')&' + -quantum + ')'; } return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum; }, @@ -145,29 +141,32 @@ var Runtime = { return l + h; }, - //! Returns the size of a type, as C/C++ would have it (in 32-bit, for now), in bytes. + //! Returns the size of a type, as C/C++ would have it (in 32-bit), in bytes. //! @param type The type, by name. - getNativeTypeSize: function(type, quantumSize) { - if (Runtime.QUANTUM_SIZE == 1) return 1; - var size = { - '%i1': 1, - '%i8': 1, - '%i16': 2, - '%i32': 4, - '%i64': 8, - "%float": 4, - "%double": 8 - }['%'+type]; // add '%' since float and double confuse Closure compiler as keys, and also spidermonkey as a compiler will remove 's from '_i8' etc - if (!size) { - if (type.charAt(type.length-1) == '*') { - size = Runtime.QUANTUM_SIZE; // A pointer - } else if (type[0] == 'i') { - var bits = parseInt(type.substr(1)); - assert(bits % 8 == 0); - size = bits/8; + getNativeTypeSize: function(type) { +#if QUANTUM_SIZE == 1 + return 1; +#else + switch (type) { + case 'i1': case 'i8': return 1; + case 'i16': return 2; + case 'i32': return 4; + case 'i64': return 8; + case 'float': return 4; + case 'double': return 8; + default: { + if (type[type.length-1] === '*') { + return Runtime.QUANTUM_SIZE; // A pointer + } else if (type[0] === 'i') { + var bits = parseInt(type.substr(1)); + assert(bits % 8 === 0); + return bits/8; + } else { + return 0; + } } } - return size; +#endif }, //! Returns the size of a structure field, as C/C++ would have it (in 32-bit, @@ -186,8 +185,11 @@ var Runtime = { // type can be a native type or a struct (or null, for structs we only look at size here) getAlignSize: function(type, size, vararg) { // we align i64s and doubles on 64-bit boundaries, unlike x86 +#if TARGET_LE32 == 1 + if (vararg) return 8; +#endif #if TARGET_LE32 - if (type == 'i64' || type == 'double' || vararg) return 8; + if (!vararg && (type == 'i64' || type == 'double')) return 8; if (!type) return Math.min(size, 8); // align structures internally to 64 bits #endif return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE); @@ -201,20 +203,42 @@ var Runtime = { type.alignSize = 0; var diffs = []; var prev = -1; + var index = 0; type.flatIndexes = type.fields.map(function(field) { + index++; var size, alignSize; if (Runtime.isNumberType(field) || Runtime.isPointerType(field)) { size = Runtime.getNativeTypeSize(field); // pack char; char; in structs, also char[X]s. alignSize = Runtime.getAlignSize(field, size); } else if (Runtime.isStructType(field)) { - size = Types.types[field].flatSize; - alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize); + if (field[1] === '0') { + // this is [0 x something]. When inside another structure like here, it must be at the end, + // and it adds no size + // XXX this happens in java-nbody for example... assert(index === type.fields.length, 'zero-length in the middle!'); + size = 0; + if (Types.types[field]) { + alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize); + } else { + alignSize = type.alignSize || QUANTUM_SIZE; + } + } else { + size = Types.types[field].flatSize; + alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize); + } } else if (field[0] == 'b') { // bN, large number field, like a [N x i8] size = field.substr(1)|0; alignSize = 1; + } else if (field[0] === '<') { + // vector type + size = alignSize = Types.types[field].flatSize; // fully aligned + } else if (field[0] === 'i') { + // illegal integer field, that could not be legalized because it is an internal structure field + // it is ok to have such fields, if we just use them as markers of field size and nothing more complex + size = alignSize = parseInt(field.substr(1))/8; + assert(size % 1 === 0, 'cannot handle non-byte-size field ' + field); } else { - throw 'Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_]); + assert(false, 'invalid type for calculateStructAlignment'); } if (type.packed) alignSize = 1; type.alignSize = Math.max(type.alignSize, alignSize); @@ -226,6 +250,11 @@ var Runtime = { prev = curr; return curr; }); + if (type.name_ && type.name_[0] === '[') { + // arrays have 2 elements, so we get the proper difference. then we scale here. that way we avoid + // allocating a potentially huge array for [999999 x i8] etc. + type.flatSize = parseInt(type.name_.substr(1))*type.flatSize/2; + } type.flatSize = Runtime.alignMemory(type.flatSize, type.alignSize); if (diffs.length == 0) { type.flatFactor = type.flatSize; @@ -332,28 +361,51 @@ var Runtime = { for (var i = 0; i < Runtime.functionPointers.length; i++) { if (!Runtime.functionPointers[i]) { Runtime.functionPointers[i] = func; - return 2 + 2*i; + return {{{ FUNCTION_POINTER_ALIGNMENT }}}*(1 + i); } } throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.'; #else var table = FUNCTION_TABLE; var ret = table.length; + assert(ret % {{{ FUNCTION_POINTER_ALIGNMENT }}} === 0); table.push(func); - table.push(0); + for (var i = 0; i < {{{ FUNCTION_POINTER_ALIGNMENT }}}-1; i++) table.push(0); return ret; #endif }, removeFunction: function(index) { #if ASM_JS - Runtime.functionPointers[(index-2)/2] = null; + Runtime.functionPointers[(index-{{{ FUNCTION_POINTER_ALIGNMENT }}})/{{{ FUNCTION_POINTER_ALIGNMENT }}}] = null; #else var table = FUNCTION_TABLE; table[index] = null; #endif }, + getAsmConst: function(code, numArgs) { + // code is a constant string on the heap, so we can cache these + if (!Runtime.asmConstCache) Runtime.asmConstCache = {}; + var func = Runtime.asmConstCache[code]; + if (func) return func; + var args = []; + for (var i = 0; i < numArgs; i++) { + args.push(String.fromCharCode(36) + i); // $0, $1 etc + } + code = Pointer_stringify(code); + if (code[0] === '"') { + // tolerate EM_ASM("..code..") even though EM_ASM(..code..) is correct + if (code.indexOf('"', 1) === code.length-1) { + code = code.substr(1, code.length-2); + } else { + // something invalid happened, e.g. EM_ASM("..code($0)..", input) + abort('invalid EM_ASM input |' + code + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)'); + } + } + return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + code + ' })'); // new Function does not allow upvars in node + }, + warnOnce: function(text) { if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {}; if (!Runtime.warnOnce.shown[text]) { @@ -367,7 +419,7 @@ var Runtime = { getFuncWrapper: function(func, sig) { assert(sig); if (!Runtime.funcWrappers[func]) { - Runtime.funcWrappers[func] = function() { + Runtime.funcWrappers[func] = function dynCall_wrapper() { return Runtime.dynCall(sig, func, arguments); }; } @@ -376,40 +428,56 @@ var Runtime = { // Returns a processor of UTF. // processCChar() receives characters from a C-like UTF representation and returns JS string fragments. + // See RFC3629 for details, the bytes are assumed to be valid UTF-8 // processJSString() receives a JS string and returns a C-like UTF representation in an array UTF8Processor: function() { var buffer = []; var needed = 0; this.processCChar = function (code) { - code = code & 0xff; - if (needed) { - buffer.push(code); - needed--; - } + code = code & 0xFF; + if (buffer.length == 0) { - if (code < 128) return String.fromCharCode(code); + if ((code & 0x80) == 0x00) { // 0xxxxxxx + return String.fromCharCode(code); + } buffer.push(code); - if (code > 191 && code < 224) { + if ((code & 0xE0) == 0xC0) { // 110xxxxx needed = 1; - } else { + } else if ((code & 0xF0) == 0xE0) { // 1110xxxx needed = 2; + } else { // 11110xxx + needed = 3; } return ''; } - if (needed > 0) return ''; + + if (needed) { + buffer.push(code); + needed--; + if (needed > 0) return ''; + } + var c1 = buffer[0]; var c2 = buffer[1]; var c3 = buffer[2]; + var c4 = buffer[3]; var ret; - if (c1 > 191 && c1 < 224) { - ret = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63)); + if (buffer.length == 2) { + ret = String.fromCharCode(((c1 & 0x1F) << 6) | (c2 & 0x3F)); + } else if (buffer.length == 3) { + ret = String.fromCharCode(((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)); } else { - ret = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + var codePoint = ((c1 & 0x07) << 18) | ((c2 & 0x3F) << 12) | + ((c3 & 0x3F) << 6) | (c4 & 0x3F); + ret = String.fromCharCode( + Math.floor((codePoint - 0x10000) / 0x400) + 0xD800, + (codePoint - 0x10000) % 0x400 + 0xDC00); } buffer.length = 0; return ret; } - this.processJSString = function(string) { + this.processJSString = function processJSString(string) { string = unescape(encodeURIComponent(string)); var ret = []; for (var i = 0; i < string.length; i++) { @@ -497,7 +565,7 @@ function getRuntime() { // Converts a value we have as signed, into an unsigned value. For // example, -1 in int32 would be a very large number as unsigned. -function unSign(value, bits, ignore, sig) { +function unSign(value, bits, ignore) { if (value >= 0) { return value; } @@ -510,7 +578,7 @@ function unSign(value, bits, ignore, sig) { // Converts a value we have as unsigned, into a signed value. For // example, 200 in a uint8 would be a negative number. -function reSign(value, bits, ignore, sig) { +function reSign(value, bits, ignore) { if (value <= 0) { return value; } |