aboutsummaryrefslogtreecommitdiff
path: root/src/parseTools.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/parseTools.js')
-rw-r--r--src/parseTools.js128
1 files changed, 52 insertions, 76 deletions
diff --git a/src/parseTools.js b/src/parseTools.js
index a076c862..48274cd5 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -78,6 +78,7 @@ function toNiceIdent(ident) {
assert(ident);
if (parseFloat(ident) == ident) return ident;
if (ident == 'null') return '0'; // see parseNumerical
+ if (ident == 'undef') return '0';
return ident.replace('%', '$').replace(/["&\\ \.@:<>,\*\[\]\(\)-]/g, '_');
}
@@ -103,6 +104,11 @@ function isNiceIdent(ident, loose) {
}
}
+function isJSVar(ident) {
+ return /^\(?[$_]?[\w$_\d ]*\)+$/.test(ident);
+
+}
+
function isStructPointerType(type) {
// This test is necessary for clang - in llvm-gcc, we
// could check for %struct. The downside is that %1 can
@@ -687,7 +693,7 @@ function makeCopyI64(value) {
function parseArbitraryInt(str, bits) {
// We parse the string into a vector of digits, base 10. This is convenient to work on.
- assert(bits % 32 == 0 || ('i' + (bits % 32)) in Runtime.INT_TYPES, 'Arbitrary-sized ints must tails that are of legal size');
+ assert(bits > 0); // NB: we don't check that the value in str can fit in this amount of bits
function str2vec(s) { // index 0 is the highest value
var ret = [];
@@ -977,27 +983,27 @@ function checkSafeHeap() {
return SAFE_HEAP === 1 || checkSpecificSafeHeap();
}
-if (ASM_JS) {
- var hexMemoryMask = '0x' + (TOTAL_MEMORY-1).toString(16);
- var decMemoryMask = (TOTAL_MEMORY-1).toString();
- var memoryMask = hexMemoryMask.length <= decMemoryMask.length ? hexMemoryMask : decMemoryMask;
-}
-
function getHeapOffset(offset, type, forceAsm) {
if (USE_TYPED_ARRAYS !== 2) {
return offset;
- } else {
- if (Runtime.getNativeFieldSize(type) > 4) {
- type = 'i32'; // XXX we emulate 64-bit values as 32
- }
- var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2;
- offset = '(' + offset + ')';
- if (ASM_JS && (phase == 'funcs' || forceAsm)) offset = '(' + offset + '&' + memoryMask + ')';
- if (shifts != 0) {
- return '(' + offset + '>>' + shifts + ')';
+ }
+
+ if (Runtime.getNativeFieldSize(type) > 4) {
+ type = 'i32'; // XXX we emulate 64-bit values as 32
+ }
+
+ var sz = Runtime.getNativeTypeSize(type);
+ var shifts = Math.log(sz)/Math.LN2;
+ offset = '(' + offset + ')';
+ if (shifts != 0) {
+ if (CHECK_HEAP_ALIGN) {
+ return '(CHECK_ALIGN_' + sz + '(' + offset + ')>>' + shifts + ')';
} else {
- return offset;
+ return '(' + offset + '>>' + shifts + ')';
}
+ } else {
+ // we need to guard against overflows here, HEAP[U]8 expects a guaranteed int
+ return isJSVar(offset) ? offset : '(' + offset + '|0)';
}
}
@@ -1046,20 +1052,6 @@ function asmCoercion(value, type, signedness) {
}
}
-var TWO_TWENTY = Math.pow(2, 20);
-
-function asmMultiplyI32(a, b) {
- // special-case: there is no integer multiply in asm, because there is no true integer
- // multiply in JS. While we wait for Math.imul, do double multiply
- if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY)) {
- return '(((' + a + ')*(' + b + '))&-1)'; // small enough to emit directly as a multiply
- }
- if (USE_MATH_IMUL) {
- return 'Math.imul(' + a + ',' + b + ')';
- }
- return '(~~(+((' + a + ')|0) * +((' + b + ')|0)))';
-}
-
function asmFloatToInt(x) {
return '(~~(' + x + '))';
}
@@ -1071,7 +1063,6 @@ function makeGetTempDouble(i, type, forSet) { // get an aliased part of the temp
var ptr = getFastValue('tempDoublePtr', '+', Runtime.getNativeTypeSize(type)*i);
var offset;
if (type == 'double') {
- if (ASM_JS) ptr = '(' + ptr + ')&' + memoryMask;
offset = '(' + ptr + ')>>3';
} else {
offset = getHeapOffset(ptr, type);
@@ -1154,8 +1145,8 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
}
}
-function makeGetValueAsm(ptr, pos, type) {
- return makeGetValue(ptr, pos, type, null, null, null, null, null, true);
+function makeGetValueAsm(ptr, pos, type, unsigned) {
+ return makeGetValue(ptr, pos, type, null, unsigned, null, null, null, true);
}
function indexizeFunctions(value, type) {
@@ -1258,7 +1249,6 @@ function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSa
return makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign, true);
}
-var SEEK_OPTIMAL_ALIGN_MIN = 20;
var UNROLL_LOOP_MAX = 8;
function makeSetValues(ptr, pos, value, type, num, align) {
@@ -1278,7 +1268,7 @@ function makeSetValues(ptr, pos, value, type, num, align) {
} else { // USE_TYPED_ARRAYS == 2
// If we don't know how to handle this at compile-time, or handling it is best done in a large amount of code, call memset
// TODO: optimize the case of numeric num but non-numeric value
- if (!isNumber(num) || !isNumber(value) || (align < 4 && parseInt(num) >= SEEK_OPTIMAL_ALIGN_MIN)) {
+ if (!isNumber(num) || !isNumber(value) || (parseInt(num)/align >= UNROLL_LOOP_MAX)) {
return '_memset(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + ', ' + asmCoercion(value, 'i32') + ', ' + asmCoercion(num, 'i32') + ')';
}
num = parseInt(num);
@@ -1293,13 +1283,7 @@ function makeSetValues(ptr, pos, value, type, num, align) {
[4, 2, 1].forEach(function(possibleAlign) {
if (num == 0) return;
if (align >= possibleAlign) {
- if (num <= UNROLL_LOOP_MAX*possibleAlign || ASM_JS) { // XXX test asm performance
- ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign, values[possibleAlign]));
- } else {
- ret.push('for (var $$dest = ' + getFastValue(ptr, '+', pos) + (possibleAlign > 1 ? '>>' + log2(possibleAlign) : '') + ', ' +
- '$$stop = $$dest + ' + Math.floor(num/possibleAlign) + '; $$dest < $$stop; $$dest++) {\n' +
- ' HEAP' + (possibleAlign*8) + '[$$dest] = ' + values[possibleAlign] + '\n}');
- }
+ ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign, values[possibleAlign]));
pos = getFastValue(pos, '+', Math.floor(num/possibleAlign)*possibleAlign);
num %= possibleAlign;
}
@@ -1336,7 +1320,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) {
unroll(type, 1) + ' }';
} else { // USE_TYPED_ARRAYS == 2
// If we don't know how to handle this at compile-time, or handling it is best done in a large amount of code, call memset
- if (!isNumber(num) || (align < 4 && parseInt(num) >= SEEK_OPTIMAL_ALIGN_MIN)) {
+ if (!isNumber(num) || (parseInt(num)/align >= UNROLL_LOOP_MAX)) {
return '_memcpy(' + dest + ', ' + src + ', ' + num + ')';
}
num = parseInt(num);
@@ -1344,16 +1328,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) {
[4, 2, 1].forEach(function(possibleAlign) {
if (num == 0) return;
if (align >= possibleAlign) {
- // If we can unroll the loop, do so. Also do so if we must unroll it (we do not create real loops when inlined)
- if (num <= UNROLL_LOOP_MAX*possibleAlign || sep == ',' || ASM_JS) { // XXX test asm performance
- ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign));
- } else {
- assert(sep == ';');
- ret.push('for (var $$src = ' + src + (possibleAlign > 1 ? '>>' + log2(possibleAlign) : '') + ', ' +
- '$$dest = ' + dest + (possibleAlign > 1 ? '>>' + log2(possibleAlign) : '') + ', ' +
- '$$stop = $$src + ' + Math.floor(num/possibleAlign) + '; $$src < $$stop; $$src++, $$dest++) {\n' +
- ' HEAP' + (possibleAlign*8) + '[$$dest] = HEAP' + (possibleAlign*8) + '[$$src]\n}');
- }
+ ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign));
src = getFastValue(src, '+', Math.floor(num/possibleAlign)*possibleAlign);
dest = getFastValue(dest, '+', Math.floor(num/possibleAlign)*possibleAlign);
num %= possibleAlign;
@@ -1373,17 +1348,23 @@ function makeHEAPView(which, start, end) {
var PLUS_MUL = set('+', '*');
var MUL_DIV = set('*', '/');
var PLUS_MINUS = set('+', '-');
+var TWO_TWENTY = Math.pow(2, 20);
// Given two values and an operation, returns the result of that operation.
// Tries to do as much as possible at compile time.
+// Leaves overflows etc. unhandled, *except* for integer multiply, in order to be efficient with Math.imul
function getFastValue(a, op, b, type) {
a = a.toString();
b = b.toString();
+ a = a == 'true' ? '1' : (a == 'false' ? '0' : a);
+ b = b == 'true' ? '1' : (b == 'false' ? '0' : b);
if (isNumber(a) && isNumber(b)) {
if (op == 'pow') {
return Math.pow(a, b).toString();
} else {
- return eval(a + op + '(' + b + ')').toString(); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12"
+ var value = eval(a + op + '(' + b + ')'); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12"
+ if (op == '/' && type in Runtime.INT_TYPES) value = value|0; // avoid emitting floats
+ return value.toString();
}
}
if (op == 'pow') {
@@ -1411,8 +1392,14 @@ function getFastValue(a, op, b, type) {
return '(' + a + '<<' + shifts + ')';
}
}
- if (ASM_JS && !(type in Runtime.FLOAT_TYPES)) {
- return asmMultiplyI32(a, b); // unoptimized multiply, do it using asm.js's special multiply operation
+ if (!(type in Runtime.FLOAT_TYPES)) {
+ // if guaranteed small enough to not overflow into a double, do a normal multiply
+ var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes
+ // Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there
+ if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) {
+ return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this
+ }
+ return 'Math.imul(' + a + ',' + b + ')';
}
} else {
if (a == '0') {
@@ -1557,7 +1544,7 @@ function makePointer(slab, pos, allocator, type, ptr) {
var ret = '';
var index = 0;
while (index < array.length) {
- ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')' : '');
+ ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')\n' : '');
index += chunkSize;
}
return ret;
@@ -1718,9 +1705,7 @@ function handleOverflow(text, bits) {
if (!bits) return text;
var correct = correctOverflows();
warnOnce(!correct || bits <= 32, 'Cannot correct overflows of this many bits: ' + bits);
- if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow() && !PGO) + (
- PGO ? ', "' + Debugging.getIdentifier() + '"' : ''
- ) + ')';
+ if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow()) + ')';
if (!correct) return text;
if (bits == 32) {
return '((' + text + ')|0)';
@@ -1828,9 +1813,7 @@ function makeSignOp(value, type, op, force, ignore) {
var bits, full;
if (type in Runtime.INT_TYPES) {
bits = parseInt(type.substr(1));
- full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign() && !PGO)) + (
- PGO ? ', "' + (ignore ? '' : Debugging.getIdentifier()) + '"' : ''
- ) + ')';
+ full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign())) + ')';
// Always sign/unsign constants at compile time, regardless of CHECK/CORRECT
if (isNumber(value)) {
return eval(full).toString();
@@ -1842,9 +1825,10 @@ function makeSignOp(value, type, op, force, ignore) {
if (!CHECK_SIGNS || ignore) {
if (bits === 32) {
if (op === 're') {
- return '((' + value + ')|0)';
+ return '(' + getFastValue(value, '|', '0') + ')';
} else {
- return '((' + value + ')>>>0)';
+
+ return '(' + getFastValue(value, '>>>', '0') + ')';
// Alternatively, we can consider the lengthier
// return makeInlineCalculation('VALUE >= 0 ? VALUE : ' + Math.pow(2, bits) + ' + VALUE', value, 'tempBigInt');
// which does not always turn us into a 32-bit *un*signed value
@@ -1853,7 +1837,7 @@ function makeSignOp(value, type, op, force, ignore) {
if (op === 're') {
return makeInlineCalculation('(VALUE << ' + (32-bits) + ') >> ' + (32-bits), value, 'tempInt');
} else {
- return '((' + value + ')&' + (Math.pow(2, bits)-1) + ')';
+ return '(' + getFastValue(value, '&', Math.pow(2, bits)-1) + ')';
}
} else { // bits > 32
if (op === 're') {
@@ -2143,14 +2127,7 @@ function processMathop(item) {
case 'add': return handleOverflow(getFastValue(idents[0], '+', idents[1], item.type), bits);
case 'sub': return handleOverflow(getFastValue(idents[0], '-', idents[1], item.type), bits);
case 'sdiv': case 'udiv': return makeRounding(getFastValue(idents[0], '/', idents[1], item.type), bits, op[0] === 's');
- case 'mul': {
- if (bits == 32 && PRECISE_I32_MUL) {
- Types.preciseI64MathUsed = true;
- return '(i64Math' + (ASM_JS ? '_' : '.') + 'multiply(' + asmCoercion(idents[0], 'i32') + ',0,' + asmCoercion(idents[1], 'i32') + ',0),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')';
- } else {
- return '((' +getFastValue(idents[0], '*', idents[1], item.type) + ')&-1)'; // force a non-eliminatable coercion here, to prevent a double result from leaking
- }
- }
+ case 'mul': return getFastValue(idents[0], '*', idents[1], item.type); // overflow handling is already done in getFastValue for '*'
case 'urem': case 'srem': return getFastValue(idents[0], '%', idents[1], item.type);
case 'or': {
if (bits > 32) {
@@ -2211,7 +2188,6 @@ function processMathop(item) {
case 'ne': case 'eq': {
// We must sign them, so we do not compare -1 to 255 (could have unsigned them both too)
// since LLVM tells us if <=, >= etc. comparisons are signed, but not == and !=.
- assert(paramTypes[0] == paramTypes[1]);
idents[0] = makeSignOp(idents[0], paramTypes[0], 're');
idents[1] = makeSignOp(idents[1], paramTypes[1], 're');
return idents[0] + (variant === 'eq' ? '==' : '!=') + idents[1];