aboutsummaryrefslogtreecommitdiff
path: root/src/parseTools.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/parseTools.js')
-rw-r--r--src/parseTools.js815
1 files changed, 519 insertions, 296 deletions
diff --git a/src/parseTools.js b/src/parseTools.js
index 89267d10..3949491e 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -6,11 +6,10 @@
// Does simple 'macro' substitution, using Django-like syntax,
// {{{ code }}} will be replaced with |eval(code)|.
function processMacros(text) {
- return text.replace(/{{{[^}]+}}}/g, function(str) {
+ return text.replace(/{{{([^}]|}(?!}))+}}}/g, function(str) {
str = str.substr(3, str.length-6);
var ret = eval(str);
- if (ret !== undefined) ret = ret.toString();
- return ret;
+ return ret ? ret.toString() : '';
});
}
@@ -66,7 +65,7 @@ function pointingLevels(type) {
var ret = 0;
var len1 = type.length - 1;
while (type[len1-ret] && type[len1-ret] === '*') {
- ret ++;
+ ret++;
}
return ret;
}
@@ -79,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, '_');
}
@@ -104,6 +104,15 @@ function isNiceIdent(ident, loose) {
}
}
+function isJSVar(ident) {
+ return /^\(?[$_]?[\w$_\d ]*\)+$/.test(ident);
+
+}
+
+function isLocalVar(ident) {
+ return ident[0] == '$';
+}
+
function isStructPointerType(type) {
// This test is necessary for clang - in llvm-gcc, we
// could check for %struct. The downside is that %1 can
@@ -112,7 +121,7 @@ function isStructPointerType(type) {
// |%5()| as a function call (like |i32 (i8*)| etc.). So
// we must check later on, in call(), where we have more
// context, to differentiate such cases.
- // A similar thing happns in isStructType()
+ // A similar thing happens in isStructType()
return !Runtime.isNumberType(type) && type[0] == '%';
}
@@ -120,9 +129,13 @@ function isPointerType(type) {
return type[type.length-1] == '*';
}
+function isArrayType(type) {
+ return /^\[\d+\ x\ (.*)\]/.test(type);
+}
+
function isStructType(type) {
if (isPointerType(type)) return false;
- if (/^\[\d+\ x\ (.*)\]/.test(type)) return true; // [15 x ?] blocks. Like structs
+ if (isArrayType(type)) return true;
if (/<?{ ?[^}]* ?}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types
// See comment in isStructPointerType()
return type[0] == '%';
@@ -155,7 +168,8 @@ function isIntImplemented(type) {
}
// Note: works for iX types and structure types, not pointers (even though they are implemented as ints)
-function getBits(type) {
+function getBits(type, allowPointers) {
+ if (allowPointers && isPointerType(type)) return 32;
if (!type) return 0;
if (type[0] == 'i') {
var left = type.substr(1);
@@ -167,11 +181,23 @@ function getBits(type) {
}
if (isStructType(type)) {
var typeData = Types.types[type];
+ if (typeData === undefined) return 0;
return typeData.flatSize*8;
}
return 0;
}
+function getNumIntChunks(type) {
+ return Math.ceil(getBits(type, true)/32);
+}
+
+function isIdenticallyImplemented(type1, type2) {
+ var floats = +(type1 in Runtime.FLOAT_TYPES) + +(type2 in Runtime.FLOAT_TYPES);
+ if (floats == 2) return true;
+ if (floats == 1) return false;
+ return getNumIntChunks(type1) == getNumIntChunks(type2);
+}
+
function isIllegalType(type) {
var bits = getBits(type);
return bits > 0 && (bits >= 64 || !isPowerOfTwo(bits));
@@ -210,21 +236,44 @@ function isPossiblyFunctionType(type) {
function isFunctionType(type, out) {
if (!isPossiblyFunctionType(type)) return false;
+ type = type.substr(0, type.length-1); // remove final '*'
+ var firstOpen = type.indexOf('(');
+ if (firstOpen <= 0) return false;
type = type.replace(/"[^"]+"/g, '".."');
- var parts;
- // hackish, but quick splitting of function def parts. this must be fast as it happens a lot
- if (type[0] != '[') {
- parts = type.split(' ');
+ var lastOpen = type.lastIndexOf('(');
+ var returnType;
+ if (firstOpen == lastOpen) {
+ returnType = getReturnType(type);
+ if (!isType(returnType)) return false;
} else {
- var index = type.search(']');
- index += type.substr(index).search(' ');
- parts = [type.substr(0, index), type.substr(index+1)];
+ returnType = 'i8*'; // some pointer type, no point in analyzing further
+ }
+ if (out) out.returnType = returnType;
+ // find ( that starts the arguments
+ var depth = 0, i = type.length-1, argText = null;
+ while (i >= 0) {
+ var curr = type[i];
+ if (curr == ')') depth++;
+ else if (curr == '(') {
+ depth--;
+ if (depth == 0) {
+ argText = type.substr(i);
+ break;
+ }
+ }
+ i--;
}
- if (pointingLevels(type) !== 1) return false;
- var text = removeAllPointing(parts.slice(1).join(' '));
- if (!text) return false;
- if (out) out.returnType = parts[0];
- return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }, out);
+ assert(argText);
+ return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2), true) }, out);
+}
+
+function getReturnType(type) {
+ if (pointingLevels(type) > 1) return '*'; // the type of a call can be either the return value, or the entire function. ** or more means it is a return value
+ var lastOpen = type.lastIndexOf('(');
+ if (lastOpen > 0) {
+ return type.substr(0, lastOpen-1);
+ }
+ return type;
}
var isTypeCache = {}; // quite hot, optimize as much as possible
@@ -241,11 +290,22 @@ function isVarArgsFunctionType(type) {
return type.substr(-varArgsSuffix.length) == varArgsSuffix;
}
-function countNormalArgs(type) {
- var out = {};
+function getNumLegalizedVars(type) { // how many legalized variables are needed to represent this type
+ if (type in Runtime.FLOAT_TYPES) return 1;
+ return Math.max(getNumIntChunks(type), 1);
+}
+
+function countNormalArgs(type, out, legalized) {
+ out = out || {};
if (!isFunctionType(type, out)) return -1;
- if (isVarArgsFunctionType(type)) out.numArgs--;
- return out.numArgs;
+ var ret = 0;
+ if (out.segments) {
+ for (var i = 0; i < out.segments.length; i++) {
+ ret += legalized ? getNumLegalizedVars(out.segments[i][0].text) : 1;
+ }
+ }
+ if (isVarArgsFunctionType(type)) ret--;
+ return ret;
}
function addIdent(token) {
@@ -404,8 +464,7 @@ function isIndexableGlobal(ident) {
return false;
}
var data = Variables.globals[ident];
- // in asm.js, externals are just globals
- return !data.alias && (ASM_JS || !data.external);
+ return !data.alias && !data.external;
}
function makeGlobalDef(ident) {
@@ -421,8 +480,7 @@ function makeGlobalUse(ident) {
UNINDEXABLE_GLOBALS[ident] = 1;
return ident;
}
- // We know and assert on TOTAL_STACK being equal to GLOBAL_BASE
- return (TOTAL_STACK + index).toString();
+ return (Runtime.GLOBAL_BASE + index).toString();
}
return ident;
}
@@ -493,7 +551,7 @@ function parseLLVMSegment(segment) {
return {
intertype: 'value',
ident: toNiceIdent(segment[1].text),
- type: segment[0].text
+ type: type
};
}
}
@@ -557,7 +615,9 @@ function parseLLVMFunctionCall(segment) {
function eatLLVMIdent(tokens) {
var ret;
if (tokens[0].text in PARSABLE_LLVM_FUNCTIONS) {
- ret = parseLLVMFunctionCall([{text: 'i0'}].concat(tokens.slice(0,2))).ident; // TODO: Handle more cases, return a full object, process it later
+ var item = parseLLVMFunctionCall([{text: '?'}].concat(tokens.slice(0,2))); // TODO: Handle more cases, return a full object, process it later
+ if (item.intertype == 'bitcast') checkBitcast(item);
+ ret = item.ident;
tokens.shift();
tokens.shift();
} else {
@@ -571,7 +631,7 @@ function cleanOutTokens(filterOut, tokens, indexes) {
if (typeof indexes !== 'object') indexes = [indexes];
for (var i = indexes.length-1; i >=0; i--) {
var index = indexes[i];
- while (tokens[index].text in filterOut) {
+ while (index < tokens.length && tokens[index].text in filterOut) {
tokens.splice(index, 1);
}
}
@@ -592,6 +652,7 @@ function IEEEUnHex(stringy) {
while (stringy.length < 16) stringy = '0' + stringy;
if (FAKE_X86_FP80 && stringy.length > 16) {
stringy = stringy.substr(stringy.length-16, 16);
+ assert(TARGET_X86, 'must only see >64 bit floats in x86, as fp80s');
warnOnce('.ll contains floating-point values with more than 64 bits. Faking values for them. If they are used, this will almost certainly break horribly!');
}
assert(stringy.length === 16, 'Can only unhex 16-digit double numbers, nothing platform-specific'); // |long double| can cause x86_fp80 which causes this
@@ -642,14 +703,16 @@ function makeI64(low, high) {
// Splits a number (an integer in a double, possibly > 32 bits) into an USE_TYPED_ARRAYS == 2 i64 value.
// Will suffer from rounding. mergeI64 does the opposite.
-function splitI64(value) {
+function splitI64(value, floatConversion) {
// We need to min here, since our input might be a double, and large values are rounded, so they can
// be slightly higher than expected. And if we get 4294967296, that will turn into a 0 if put into a
// HEAP32 or |0'd, etc.
+ var lowInput = legalizedI64s ? value : 'VALUE';
+ if (floatConversion && ASM_JS) lowInput = asmFloatToInt(lowInput);
if (legalizedI64s) {
- return [value + '>>>0', 'Math.min(Math.floor((' + value + ')/4294967296), 4294967295)'];
+ return [lowInput + '>>>0', 'Math.min(Math.floor((' + value + ')/' + asmEnsureFloat(4294967296, 'float') + '), ' + asmEnsureFloat(4294967295, 'float') + ')>>>0'];
} else {
- return makeInlineCalculation(makeI64('VALUE>>>0', 'Math.min(Math.floor(VALUE/4294967296), 4294967295)'), value, 'tempBigIntP');
+ return makeInlineCalculation(makeI64(lowInput + '>>>0', 'Math.min(Math.floor(VALUE/' + asmEnsureFloat(4294967296, 'float') + '), ' + asmEnsureFloat(4294967295, 'float') + ')>>>0'), value, 'tempBigIntP');
}
}
function mergeI64(value, unsigned) {
@@ -678,7 +741,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 = [];
@@ -803,7 +866,9 @@ function parseNumerical(value, type) {
return '0';
}
if (isNumber(value)) {
- return parseFloat(value).toString(); // will change e.g. 5.000000e+01 to 50
+ var ret = parseFloat(value); // will change e.g. 5.000000e+01 to 50
+ if (type in Runtime.FLOAT_TYPES && value[0] == '-' && ret === 0) return '-0'; // fix negative 0, toString makes it 0
+ return ret.toString();
} else {
return value;
}
@@ -968,27 +1033,29 @@ 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) {
+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
+ }
+
+ if (Runtime.getNativeFieldSize(type) > 4) {
+ if (type == 'i64' || TARGET_X86) {
+ type = 'i32'; // XXX we emulate 64-bit values as 32 in x86, and also in le32 but only i64, not double
}
- var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2;
- offset = '(' + offset + ')';
- if (ASM_JS && phase == 'funcs') offset = '(' + offset + '&' + memoryMask + ')';
- if (shifts != 0) {
- return '(' + offset + '>>' + shifts + ')';
+ }
+
+ 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 + '|0)>>' + 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)';
}
}
@@ -999,7 +1066,8 @@ function makeVarDef(js) {
function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
if (!ASM_JS) return value;
- if (!isIntImplemented(type) && isNumber(value) && value.toString().indexOf('.') < 0) {
+ // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
+ if (type in Runtime.FLOAT_TYPES && isNumber(value) && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
return '(+(' + value + '))';
} else {
return value;
@@ -1007,31 +1075,66 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
}
function asmInitializer(type, impl) {
- if (isIntImplemented(type)) {// || (impl && impl == 'VAR_EMULATED')) {
- return '0';
- } else {
+ if (type in Runtime.FLOAT_TYPES) {
return '+0';
+ } else {
+ return '0';
}
}
-function asmCoercion(value, type) {
+function asmCoercion(value, type, signedness) {
if (!ASM_JS) return value;
if (type == 'void') {
return value;
- } else if (isIntImplemented(type)) {
+ } else if (type in Runtime.FLOAT_TYPES) {
+ if (isNumber(value)) {
+ return asmEnsureFloat(value, type);
+ } else {
+ if (signedness) {
+ if (signedness == 'u') {
+ value = '(' + value + ')>>>0';
+ } else {
+ value = '(' + value + ')|0';
+ }
+ }
+ return '(+(' + value + '))';
+ }
+ } else {
return '((' + value + ')|0)';
+ }
+}
+
+function asmFloatToInt(x) {
+ return '(~~(' + x + '))';
+}
+
+function makeGetTempDouble(i, type, forSet) { // get an aliased part of the tempDouble temporary storage
+ // Cannot use makeGetValue because it uses us
+ // this is a unique case where we *can* use HEAPF64
+ var slab = type == 'double' ? 'HEAPF64' : makeGetSlabs(null, type)[0];
+ var ptr = getFastValue('tempDoublePtr', '+', Runtime.getNativeTypeSize(type)*i);
+ var offset;
+ if (type == 'double') {
+ offset = '(' + ptr + ')>>3';
} else {
- return '(+(' + value + '))';
+ offset = getHeapOffset(ptr, type);
}
+ var ret = slab + '[' + offset + ']';
+ if (!forSet) ret = asmCoercion(ret, type);
+ return ret;
}
-function makeGetTempDouble(i) { // TODO: Support other than i32
- return makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32')*i, 'i32');
+function makeSetTempDouble(i, type, value) {
+ return makeGetTempDouble(i, type, true) + '=' + asmEnsureFloat(value, type);
}
+var asmPrintCounter = 0;
+
// See makeSetValue
-function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe) {
+function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe, forceAsm) {
if (UNALIGNED_MEMORY) align = 1;
+ else if (FORCE_ALIGNED_MEMORY && !isIllegalType(type)) align = 8;
+
if (isStructType(type)) {
var typeData = Types.types[type];
var ret = [];
@@ -1041,23 +1144,26 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
return '{ ' + ret.join(', ') + ' }';
}
- if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') {
- return '(HEAP32[tempDoublePtr>>2]=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align) + ',' +
- 'HEAP32[tempDoublePtr+4>>2]=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align) + ',' +
- 'HEAPF64[tempDoublePtr>>3])';
+ // In double mode 1, in x86 we always assume unaligned because we can't trust that; otherwise in le32
+ // we need this code path if we are not fully aligned.
+ if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double' && (TARGET_X86 || align < 8)) {
+ return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align)) + ',' +
+ makeSetTempDouble(1, 'i32', makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align)) + ',' +
+ makeGetTempDouble(0, 'double') + ')';
}
if (USE_TYPED_ARRAYS == 2 && align) {
// Alignment is important here. May need to split this up
var bytes = Runtime.getNativeTypeSize(type);
+ if (DOUBLE_MODE == 0 && type == 'double') bytes = 4; // we will really only read 4 bytes here
if (bytes > align) {
var ret = '(';
if (isIntImplemented(type)) {
if (bytes == 4 && align == 2) {
// Special case that we can optimize
- ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '+' +
+ ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '|' +
'(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore) + '<<16)';
- } else { // XXX we cannot truly handle > 4...
+ } else { // XXX we cannot truly handle > 4... (in x86)
ret = '';
for (var i = 0; i < bytes; i++) {
ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore) + (i > 0 ? '<<' + (8*i) : '') + ')';
@@ -1067,9 +1173,9 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
}
} else {
if (type == 'float') {
- ret += 'copyTempFloat(' + getFastValue(ptr, '+', pos) + '),HEAPF32[tempDoublePtr>>2]';
+ ret += 'copyTempFloat(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + '),' + makeGetTempDouble(0, 'float');
} else {
- ret += 'copyTempDouble(' + getFastValue(ptr, '+', pos) + '),HEAP64[tempDoublePtr>>3]';
+ ret += 'copyTempDouble(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + '),' + makeGetTempDouble(0, 'double');
}
}
ret += ')';
@@ -1079,18 +1185,27 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
var offset = calcFastOffset(ptr, pos, noNeedFirst);
if (SAFE_HEAP && !noSafe) {
- if (type !== 'null' && type[0] !== '#') type = '"' + safeQuote(type) + '"';
- if (type[0] === '#') type = type.substr(1);
- return 'SAFE_HEAP_LOAD(' + offset + ', ' + type + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
+ var printType = type;
+ if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
+ if (printType[0] === '#') printType = printType.substr(1);
+ return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type);
} else {
- var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type) + ']';
- if (ASM_JS && phase == 'funcs') {
+ var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']';
+ if (ASM_JS && (phase == 'funcs' || forceAsm)) {
ret = asmCoercion(ret, type);
}
+ if (ASM_HEAP_LOG) {
+ ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret,
+ 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int'));
+ }
return ret;
}
}
+function makeGetValueAsm(ptr, pos, type, unsigned) {
+ return makeGetValue(ptr, pos, type, null, unsigned, null, null, null, true);
+}
+
function indexizeFunctions(value, type) {
assert((type && type !== '?') || (typeof value === 'string' && value.substr(0, 6) === 'CHECK_'), 'No type given for function indexizing');
assert(value !== type, 'Type set to value');
@@ -1120,8 +1235,10 @@ function indexizeFunctions(value, type) {
//! 'null' means, in the context of SAFE_HEAP, that we should accept all types;
//! which means we should write to all slabs, ignore type differences if any on reads, etc.
//! @param noNeedFirst Whether to ignore the offset in the pointer itself.
-function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
+function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign, forceAsm) {
if (UNALIGNED_MEMORY && !forcedAlign) align = 1;
+ else if (FORCE_ALIGNED_MEMORY && !isIllegalType(type)) align = 8;
+
sep = sep || ';';
if (isStructType(type)) {
var typeData = Types.types[type];
@@ -1137,10 +1254,10 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
return ret.join('; ');
}
- if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') {
- return '(HEAPF64[tempDoublePtr>>3]=' + value + ',' +
- makeSetValue(ptr, pos, 'HEAP32[tempDoublePtr>>2]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' +
- makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'HEAP32[tempDoublePtr+4>>2]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')';
+ if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double' && (TARGET_X86 || align < 8)) {
+ return '(' + makeSetTempDouble(0, 'double', value) + ',' +
+ makeSetValue(ptr, pos, makeGetTempDouble(0, 'i32'), 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' +
+ makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), makeGetTempDouble(1, 'i32'), 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')';
} else if (USE_TYPED_ARRAYS == 2 && type == 'i64') {
return '(tempI64 = [' + splitI64(value) + '],' +
makeSetValue(ptr, pos, 'tempI64[0]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' +
@@ -1152,6 +1269,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
if (USE_TYPED_ARRAYS == 2 && (align || needSplitting)) {
// Alignment is important here, or we need to split this up for other reasons.
var bytes = Runtime.getNativeTypeSize(type);
+ if (DOUBLE_MODE == 0 && type == 'double') bytes = 4; // we will really only read 4 bytes here
if (bytes > align || needSplitting) {
var ret = '';
if (isIntImplemented(type)) {
@@ -1164,7 +1282,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
ret += 'tempBigInt=' + value + sep;
for (var i = 0; i < bytes; i++) {
ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore, 1);
- if (i < bytes-1) ret += sep + 'tempBigInt>>=8' + sep;
+ if (i < bytes-1) ret += sep + 'tempBigInt = tempBigInt>>8' + sep;
}
}
} else {
@@ -1178,17 +1296,19 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
value = indexizeFunctions(value, type);
var offset = calcFastOffset(ptr, pos, noNeedFirst);
if (SAFE_HEAP && !noSafe) {
- if (type !== 'null' && type[0] !== '#') type = '"' + safeQuote(type) + '"';
- if (type[0] === '#') type = type.substr(1);
- return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ', ' + type + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
+ var printType = type;
+ if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
+ if (printType[0] === '#') printType = printType.substr(1);
+ return 'SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
} else {
- return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type) + ']=' + value }).join(sep);
- //return '(print("set:"+(' + value + ')+":"+(' + getHeapOffset(offset, type) + ')),' +
- // makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type) + ']=' + value }).join('; ') + ')';
+ return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep);
}
}
-var SEEK_OPTIMAL_ALIGN_MIN = 20;
+function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
+ return makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign, true);
+}
+
var UNROLL_LOOP_MAX = 8;
function makeSetValues(ptr, pos, value, type, num, align) {
@@ -1208,8 +1328,8 @@ 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)) {
- return '_memset(' + getFastValue(ptr, '+', pos) + ', ' + value + ', ' + num + ', ' + align + ')';
+ 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);
value = parseInt(value);
@@ -1223,13 +1343,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;
}
@@ -1266,24 +1380,21 @@ 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)) {
- return '_memcpy(' + dest + ', ' + src + ', ' + num + ', ' + align + ')';
+ if (!isNumber(num)) num = stripCorrections(num);
+ if (!isNumber(align)) align = stripCorrections(align);
+ if (!isNumber(num) || (parseInt(num)/align >= UNROLL_LOOP_MAX)) {
+ return '_memcpy(' + dest + ', ' + src + ', ' + num + ')';
}
num = parseInt(num);
+ if (ASM_JS) {
+ dest = stripCorrections(dest); // remove corrections, since we will be correcting after we add anyhow,
+ src = stripCorrections(src); // and in the heap assignment expression
+ }
var ret = [];
[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;
@@ -1303,17 +1414,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') {
@@ -1341,6 +1458,15 @@ function getFastValue(a, op, b, type) {
return '(' + a + '<<' + shifts + ')';
}
}
+ 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') {
return '0';
@@ -1394,56 +1520,132 @@ function makeGetPos(ptr) {
var IHEAP_FHEAP = set('IHEAP', 'IHEAPU', 'FHEAP');
-function makePointer(slab, pos, allocator, type, ptr) {
+var temp64f = new Float64Array(1);
+var temp32f = new Float32Array(temp64f.buffer);
+var temp32 = new Uint32Array(temp64f.buffer);
+var temp16 = new Uint16Array(temp64f.buffer);
+var temp8 = new Uint8Array(temp64f.buffer);
+var memoryInitialization = [];
+
+function writeInt8s(slab, i, value, type) {
+ var currSize;
+ switch (type) {
+ case 'i1':
+ case 'i8': temp8[0] = value; currSize = 1; break;
+ case 'i16': temp16[0] = value; currSize = 2; break;
+ case 'float': temp32f[0] = value; currSize = 4; break;
+ case 'double': temp64f[0] = value; currSize = 8; break;
+ case 'i64': // fall through, i64 is two i32 chunks
+ case 'i32': // fall through, i32 can be a pointer
+ default: {
+ if (type == 'i32' || type == 'i64' || type[type.length-1] == '*') {
+ if (!isNumber(value)) { // function table stuff, etc.
+ slab[i] = value;
+ slab[i+1] = slab[i+2] = slab[i+3] = 0;
+ return 4;
+ }
+ temp32[0] = value;
+ currSize = 4;
+ } else {
+ throw 'what? ' + types[i];
+ }
+ }
+ }
+ for (var j = 0; j < currSize; j++) {
+ slab[i+j] = temp8[j];
+ }
+ return currSize;
+}
+
+function makePointer(slab, pos, allocator, type, ptr, finalMemoryInitialization) {
assert(type, 'makePointer requires type info');
- if (slab.substr(0, 4) === 'HEAP' || (USE_TYPED_ARRAYS == 1 && slab in IHEAP_FHEAP)) return pos;
+ if (typeof slab == 'string' && (slab.substr(0, 4) === 'HEAP' || (USE_TYPED_ARRAYS == 1 && slab in IHEAP_FHEAP))) return pos;
var types = generateStructTypes(type);
- // compress type info and data if possible
- var de;
- try {
- // compress all-zeros into a number (which will become zeros(..)).
- // note that we cannot always eval the slab, e.g., if it contains ident,0,0 etc. In that case, no compression TODO: ensure we get arrays here, not str
- var evaled = typeof slab === 'string' ? eval(slab) : slab;
- de = dedup(evaled);
- if (de.length === 1 && de[0] === 0) {
- slab = types.length;
- if (USE_TYPED_ARRAYS == 2) {
- types = ['i8']; // if data is zeros, we don't need type info
+ if (typeof slab == 'object') {
+ for (var i = 0; i < slab.length; i++) {
+ var curr = slab[i];
+ if (isNumber(curr)) {
+ slab[i] = parseFloat(curr); // fix "5" to 5 etc.
+ } else if (curr == 'undef') {
+ slab[i] = 0;
}
}
- // TODO: if not all zeros, at least filter out items with type === 0. requires cleverness to know how to skip at runtime though. also
- // be careful of structure padding
- } catch(e){}
- de = dedup(types);
- if (de.length === 1) {
- types = de[0];
- } else if (de.length === 2 && typeof slab === 'number') {
- // If slab is all zeros, we can compress types even if we have i32,0,0,0,i32,0,0,0 etc. - we do not need the zeros
- de = de.filter(function(x) { return x !== 0 });
+ }
+ // compress type info and data if possible
+ if (USE_TYPED_ARRAYS != 2) {
+ var de;
+ try {
+ // compress all-zeros into a number (which will become zeros(..)).
+ // note that we cannot always eval the slab, e.g., if it contains ident,0,0 etc. In that case, no compression TODO: ensure we get arrays here, not str
+ var evaled = typeof slab === 'string' ? eval(slab) : slab;
+ de = dedup(evaled);
+ if (de.length === 1 && de[0] == 0) {
+ slab = types.length;
+ }
+ // TODO: if not all zeros, at least filter out items with type === 0. requires cleverness to know how to skip at runtime though. also
+ // be careful of structure padding
+ } catch(e){}
+ de = dedup(types);
if (de.length === 1) {
types = de[0];
+ } else if (de.length === 2 && typeof slab === 'number') {
+ // If slab is all zeros, we can compress types even if we have i32,0,0,0,i32,0,0,0 etc. - we do not need the zeros
+ de = de.filter(function(x) { return x !== 0 });
+ if (de.length === 1) {
+ types = de[0];
+ }
}
+ } else { // USE_TYPED_ARRAYS == 2
+ if (!finalMemoryInitialization) {
+ // XXX This heavily assumes the target endianness is the same as our current endianness! XXX
+ var i = 0;
+ while (i < slab.length) {
+ var currType = types[i];
+ if (!currType) { i++; continue }
+ i += writeInt8s(slab, i, slab[i], currType);
+ }
+ types = 'i8';
+ }
+ }
+ if (allocator == 'ALLOC_NONE' && USE_TYPED_ARRAYS == 2) {
+ if (!finalMemoryInitialization) {
+ // writing out into memory, without a normal allocation. We put all of these into a single big chunk.
+ assert(typeof slab == 'object');
+ assert(slab.length % QUANTUM_SIZE == 0, slab.length); // must be aligned already
+ var offset = ptr - Runtime.GLOBAL_BASE;
+ for (var i = 0; i < slab.length; i++) {
+ memoryInitialization[offset + i] = slab[i];
+ }
+ return '';
+ }
+ // This is the final memory initialization
+ types = 'i8';
}
+
// JS engines sometimes say array initializers are too large. Work around that by chunking and calling concat to combine at runtime
- var chunkSize = 10240;
+ var chunkSize = JS_CHUNK_SIZE;
function chunkify(array) {
// break very large slabs into parts
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;
}
- if (typeof slab == 'string' && evaled && evaled.length > chunkSize) {
- slab = chunkify(evaled);
+ if (typeof slab == 'object' && slab.length > chunkSize) {
+ slab = chunkify(slab);
+ }
+ if (typeof types == 'object') {
+ while (types.length < slab.length) types.push(0);
}
if (typeof types != 'string' && types.length > chunkSize) {
types = chunkify(types);
} else {
types = JSON.stringify(types);
}
+ if (typeof slab == 'object') slab = '[' + slab.join(',') + ']';
return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ')';
}
@@ -1470,7 +1672,11 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) {
case 'i1': case 'i8': return [unsigned ? 'HEAPU8' : 'HEAP8']; break;
case 'i16': return [unsigned ? 'HEAPU16' : 'HEAP16']; break;
case 'i32': case 'i64': return [unsigned ? 'HEAPU32' : 'HEAP32']; break;
- case 'float': case 'double': return ['HEAPF32']; break;
+ case 'double': {
+ if (TARGET_LE32) return ['HEAPF64']; // in le32, we do have the ability to assume 64-bit alignment
+ // otherwise, fall through to float
+ }
+ case 'float': return ['HEAPF32'];
default: {
throw 'what, exactly, can we do for unknown types in TA2?! ' + new Error().stack;
}
@@ -1479,45 +1685,48 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) {
return [];
}
-function finalizeLLVMFunctionCall(item, noIndexizeFunctions) {
- if (item.intertype == 'getelementptr') { // TODO finalizeLLVMParameter on the ident and the indexes?
- return makePointer(makeGetSlabs(item.ident, item.type)[0], getGetElementPtrIndexes(item), null, item.type);
- }
- if (item.intertype == 'bitcast') {
- // Warn about some types of casts, then fall through to the handling code below
- var oldType = item.params[0].type;
- var newType = item.type;
- if (isPossiblyFunctionType(oldType) && isPossiblyFunctionType(newType)) {
- var oldCount = countNormalArgs(oldType);
- var newCount = countNormalArgs(newType);
- if (oldCount != newCount && oldCount && newCount) {
- warnOnce('Casting a function pointer type to another with a different number of arguments. See more info in the compiler source');
- if (VERBOSE) {
- warnOnce('Casting a function pointer type to another with a different number of arguments: ' + oldType + ' vs. ' + newType + ', on ' + item.params[0].ident);
+function checkBitc