aboutsummaryrefslogtreecommitdiff
path: root/src/parseTools.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/parseTools.js')
-rw-r--r--src/parseTools.js821
1 files changed, 544 insertions, 277 deletions
diff --git a/src/parseTools.js b/src/parseTools.js
index 3949491e..bad080b7 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -5,16 +5,18 @@
// Does simple 'macro' substitution, using Django-like syntax,
// {{{ code }}} will be replaced with |eval(code)|.
+// NOTE: Be careful with that ret check. If ret is |0|, |ret ? ret.toString() : ''| would result in ''!
function processMacros(text) {
return text.replace(/{{{([^}]|}(?!}))+}}}/g, function(str) {
str = str.substr(3, str.length-6);
var ret = eval(str);
- return ret ? ret.toString() : '';
+ return ret !== null ? ret.toString() : '';
});
}
// Simple #if/else/endif preprocessing for a file. Checks if the
// ident checked is true in our global.
+// Also handles #include x.js (similar to C #include <file>)
function preprocess(text) {
var lines = text.split('\n');
var ret = '';
@@ -29,20 +31,34 @@ function preprocess(text) {
ret += line + '\n';
}
} else {
- if (line[1] && line[1] == 'i') { // if
- var parts = line.split(' ');
- var ident = parts[1];
- var op = parts[2];
- var value = parts[3];
- if (op) {
- assert(op === '==')
- showStack.push(ident in this && this[ident] == value);
- } else {
- showStack.push(ident in this && this[ident] > 0);
+ if (line[1] == 'i') {
+ if (line[2] == 'f') { // if
+ var parts = line.split(' ');
+ var ident = parts[1];
+ var op = parts[2];
+ var value = parts[3];
+ if (op) {
+ if (op === '==') {
+ showStack.push(ident in this && this[ident] == value);
+ } else if (op === '!=') {
+ showStack.push(!(ident in this && this[ident] == value));
+ } else {
+ error('unsupported preprecessor op ' + op);
+ }
+ } else {
+ if (ident[0] === '!') {
+ showStack.push(!(this[ident.substr(1)] > 0));
+ } else {
+ showStack.push(ident in this && this[ident] > 0);
+ }
+ }
+ } else if (line[2] == 'n') { // include
+ var included = read(line.substr(line.indexOf(' ')+1));
+ ret += '\n' + preprocess(included) + '\n'
}
- } else if (line[2] && line[2] == 'l') { // else
+ } else if (line[2] == 'l') { // else
showStack.push(!showStack.pop());
- } else if (line[2] && line[2] == 'n') { // endif
+ } else if (line[2] == 'n') { // endif
showStack.pop();
} else {
throw "Unclear preprocessor command: " + line;
@@ -105,12 +121,22 @@ function isNiceIdent(ident, loose) {
}
function isJSVar(ident) {
- return /^\(?[$_]?[\w$_\d ]*\)+$/.test(ident);
-
+ if (ident[0] === '(') {
+ if (ident[ident.length-1] !== ')') return false;
+ ident = ident.substr(1, ident.length-2);
+ }
+ return /^[$_]?[\w$_\d]* *$/.test(ident);
}
function isLocalVar(ident) {
- return ident[0] == '$';
+ return ident[0] === '$';
+}
+
+// Simple variables or numbers, or things already quoted, do not need to be quoted
+function needsQuoting(ident) {
+ if (/^[-+]?[$_]?[\w$_\d]*$/.test(ident)) return false; // number or variable
+ if (ident[0] === '(' && ident[ident.length-1] === ')' && ident.indexOf('(', 1) < 0) return false; // already fully quoted
+ return true;
}
function isStructPointerType(type) {
@@ -141,6 +167,10 @@ function isStructType(type) {
return type[0] == '%';
}
+function isVectorType(type) {
+ return type[type.length-1] === '>';
+}
+
function isStructuralType(type) {
return /^{ ?[^}]* ?}$/.test(type); // { i32, i8 } etc. - anonymous struct types
}
@@ -199,8 +229,22 @@ function isIdenticallyImplemented(type1, type2) {
}
function isIllegalType(type) {
- var bits = getBits(type);
- return bits > 0 && (bits >= 64 || !isPowerOfTwo(bits));
+ switch (type) {
+ case 'i1':
+ case 'i8':
+ case 'i16':
+ case 'i32':
+ case 'float':
+ case 'double':
+ case 'rawJS':
+ case '<2 x float>':
+ case '<4 x float>':
+ case '<2 x i32>':
+ case '<4 x i32>':
+ case 'void': return false;
+ }
+ if (!type || type[type.length-1] === '*') return false;
+ return true;
}
function isVoidType(type) {
@@ -214,9 +258,9 @@ function isFunctionDef(token, out) {
if (nonPointing[0] != '(' || nonPointing.substr(-1) != ')')
return false;
if (nonPointing === '()') return true;
- if (!token.item) return false;
+ if (!token.tokens) return false;
var fail = false;
- var segments = splitTokenList(token.item.tokens);
+ var segments = splitTokenList(token.tokens);
segments.forEach(function(segment) {
var subtext = segment[0].text;
fail = fail || segment.length > 1 || !(isType(subtext) || subtext == '...');
@@ -264,13 +308,16 @@ function isFunctionType(type, out) {
i--;
}
assert(argText);
- return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2), true) }, out);
+ return isFunctionDef({ text: argText, tokens: tokenize(argText.substr(1, argText.length-2)) }, 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) {
+ // handle things like void (i32)* (i32, void (i32)*)*
+ var closeStar = type.indexOf(')*');
+ if (closeStar > 0 && closeStar < type.length-2) lastOpen = closeStar+3;
return type.substr(0, lastOpen-1);
}
return type;
@@ -308,6 +355,33 @@ function countNormalArgs(type, out, legalized) {
return ret;
}
+function getVectorSize(type) {
+ return parseInt(type.substring(1, type.indexOf(' ')));
+}
+
+function getVectorNativeType(type) {
+ Types.usesSIMD = true;
+ switch (type) {
+ case '<2 x float>':
+ case '<4 x float>': return 'float';
+ case '<2 x i32>':
+ case '<4 x i32>': return 'i32';
+ default: throw 'unknown vector type ' + type;
+ }
+}
+
+function getSIMDName(type) {
+ switch (type) {
+ case 'i32': return 'int';
+ case 'float': return 'float';
+ default: throw 'getSIMDName ' + type;
+ }
+}
+
+function getVectorBaseType(type) {
+ return getSIMDName(getVectorNativeType(type));
+}
+
function addIdent(token) {
token.ident = token.text;
return token;
@@ -360,7 +434,6 @@ var SPLIT_TOKEN_LIST_SPLITTERS = set(',', 'to'); // 'to' can separate parameters
function splitTokenList(tokens) {
if (tokens.length == 0) return [];
if (!tokens.slice) tokens = tokens.tokens;
- if (tokens.slice(-1)[0].text != ',') tokens.push({text:','});
var ret = [];
var seg = [];
for (var i = 0; i < tokens.length; i++) {
@@ -370,24 +443,22 @@ function splitTokenList(tokens) {
seg = [];
} else if (token.text == ';') {
ret.push(seg);
- break;
+ return ret;
} else {
seg.push(token);
}
}
+ if (seg.length) ret.push(seg);
return ret;
}
function parseParamTokens(params) {
if (params.length === 0) return [];
var ret = [];
- if (params[params.length-1].text != ',') {
- params.push({ text: ',' });
- }
var anonymousIndex = 0;
while (params.length > 0) {
var i = 0;
- while (params[i].text != ',') i++;
+ while (i < params.length && params[i].text != ',') i++;
var segment = params.slice(0, i);
params = params.slice(i+1);
segment = cleanSegment(segment);
@@ -396,12 +467,18 @@ function parseParamTokens(params) {
// handle 'byval' and 'byval align X'. We store the alignment in 'byVal'
byVal = QUANTUM_SIZE;
segment.splice(1, 1);
+ if (segment[1] && (segment[1].text === 'nocapture' || segment[1].text === 'readonly')) {
+ segment.splice(1, 1);
+ }
if (segment[1] && segment[1].text === 'align') {
assert(isNumber(segment[2].text));
byVal = parseInt(segment[2].text);
segment.splice(1, 2);
}
}
+ if (segment[1] && (segment[1].text === 'nocapture' || segment[1].text === 'readonly')) {
+ segment.splice(1, 1);
+ }
if (segment.length == 1) {
if (segment[0].text == '...') {
ret.push({
@@ -420,24 +497,13 @@ function parseParamTokens(params) {
Types.needAnalysis[ret[ret.length-1].type] = 0;
anonymousIndex ++;
}
- } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) {
- ret.push(parseLLVMFunctionCall(segment));
- } else if (segment[1].text === 'blockaddress') {
- ret.push(parseBlockAddress(segment));
} else {
if (segment[2] && segment[2].text == 'to') { // part of bitcast params
segment = segment.slice(0, 2);
}
- while (segment.length > 2) {
- segment[0].text += segment[1].text;
- segment.splice(1, 1); // TODO: merge tokens nicely
- }
- ret.push({
- intertype: 'value',
- type: segment[0].text,
- ident: toNiceIdent(parseNumerical(segment[1].text, segment[0].text))
- });
- Types.needAnalysis[removeAllPointing(ret[ret.length-1].type)] = 0;
+ var parsed = parseLLVMSegment(segment);
+ if (parsed.intertype === 'value' && !isIllegalType(parsed.type)) parsed.ident = parseNumerical(parsed.ident, parsed.type);
+ ret.push(parsed);
}
ret[ret.length-1].byVal = byVal;
}
@@ -467,6 +533,18 @@ function isIndexableGlobal(ident) {
return !data.alias && !data.external;
}
+function isBSS(item) {
+ if (!USE_BSS) {
+ return false;
+ }
+
+ if (item.external) return false; // externals are typically implemented in a JS library, and must be accessed by name, explicitly
+
+ // return true if a global is uninitialized or initialized to 0
+ return (item.value && item.value.intertype === 'emptystruct') ||
+ (item.value && item.value.value !== undefined && item.value.value === '0');
+}
+
function makeGlobalDef(ident) {
if (!NAMED_GLOBALS && isIndexableGlobal(ident)) return '';
return 'var ' + ident + ';';
@@ -480,7 +558,9 @@ function makeGlobalUse(ident) {
UNINDEXABLE_GLOBALS[ident] = 1;
return ident;
}
- return (Runtime.GLOBAL_BASE + index).toString();
+ var ret = (Runtime.GLOBAL_BASE + index).toString();
+ if (SIDE_MODULE) ret = '(H_BASE+' + ret + ')';
+ return ret;
}
return ident;
}
@@ -490,29 +570,13 @@ function sortGlobals(globals) {
ks.sort();
var inv = invertArray(ks);
return values(globals).sort(function(a, b) {
- return inv[b.ident] - inv[a.ident];
+ // sort globals based on if they need to be explicitly initialized or not (moving
+ // values that don't need to be to the end of the array). if equal, sort by name.
+ return (Number(isBSS(a)) - Number(isBSS(b))) ||
+ (inv[b.ident] - inv[a.ident]);
});
}
-function finalizeParam(param) {
- if (param.intertype in PARSABLE_LLVM_FUNCTIONS) {
- return finalizeLLVMFunctionCall(param);
- } else if (param.intertype === 'blockaddress') {
- return finalizeBlockAddress(param);
- } else if (param.intertype === 'jsvalue') {
- return param.ident;
- } else {
- if (param.type == 'i64' && USE_TYPED_ARRAYS == 2) {
- return parseI64Constant(param.ident);
- }
- var ret = toNiceIdent(param.ident);
- if (ret in Variables.globals) {
- ret = makeGlobalUse(ret);
- }
- return ret;
- }
-}
-
// Segment ==> Parameter
function parseLLVMSegment(segment) {
var type;
@@ -547,6 +611,17 @@ function parseLLVMSegment(segment) {
return parseBlockAddress(segment);
} else {
type = segment[0].text;
+ if (type[type.length-1] === '>' && segment[1].text[0] === '<') {
+ // vector literal
+ var nativeType = getVectorNativeType(type);
+ return {
+ intertype: 'vector',
+ idents: splitTokenList(segment[1].tokens).map(function(pair) {
+ return parseNumerical(pair[1].text, nativeType);
+ }),
+ type: type
+ };
+ }
Types.needAnalysis[type] = 0;
return {
intertype: 'value',
@@ -557,7 +632,7 @@ function parseLLVMSegment(segment) {
}
function cleanSegment(segment) {
- while (segment.length >= 2 && ['noalias', 'sret', 'nocapture', 'nest', 'zeroext', 'signext'].indexOf(segment[1].text) != -1) {
+ while (segment.length >= 2 && ['noalias', 'sret', 'nocapture', 'nest', 'zeroext', 'signext', 'readnone'].indexOf(segment[1].text) != -1) {
segment.splice(1, 1);
}
return segment;
@@ -565,6 +640,8 @@ function cleanSegment(segment) {
var MATHOPS = set(['add', 'sub', 'sdiv', 'udiv', 'mul', 'icmp', 'zext', 'urem', 'srem', 'fadd', 'fsub', 'fmul', 'fdiv', 'fcmp', 'frem', 'uitofp', 'sitofp', 'fpext', 'fptrunc', 'fptoui', 'fptosi', 'trunc', 'sext', 'select', 'shl', 'shr', 'ashl', 'ashr', 'lshr', 'lshl', 'xor', 'or', 'and', 'ptrtoint', 'inttoptr']);
+var JS_MATH_BUILTINS = set(['Math_sin', 'Math_cos', 'Math_tan', 'Math_asin', 'Math_acos', 'Math_atan', 'Math_ceil', 'Math_floor', 'Math_exp', 'Math_log', 'Math_sqrt']);
+
var PARSABLE_LLVM_FUNCTIONS = set('getelementptr', 'bitcast');
mergeInto(PARSABLE_LLVM_FUNCTIONS, MATHOPS);
@@ -577,13 +654,13 @@ function parseLLVMFunctionCall(segment) {
segment = cleanSegment(segment);
// Remove additional modifiers
var variant = null;
- if (!segment[2] || !segment[2].item) {
+ if (!segment[2] || !segment[2].tokens) {
variant = segment.splice(2, 1)[0];
if (variant && variant.text) variant = variant.text; // needed for mathops
}
assertTrue(['inreg', 'byval'].indexOf(segment[1].text) == -1);
assert(segment[1].text in PARSABLE_LLVM_FUNCTIONS);
- while (!segment[2].item) {
+ while (!segment[2].tokens) {
segment.splice(2, 1); // Remove modifiers
if (!segment[2]) throw 'Invalid segment!';
}
@@ -592,15 +669,15 @@ function parseLLVMFunctionCall(segment) {
if (type === '?') {
if (intertype === 'getelementptr') {
type = '*'; // a pointer, we can easily say, this is
- } else if (segment[2].item.tokens.slice(-2)[0].text === 'to') {
- type = segment[2].item.tokens.slice(-1)[0].text;
+ } else if (segment[2].tokens.slice(-2)[0].text === 'to') {
+ type = segment[2].tokens.slice(-1)[0].text;
}
}
var ret = {
intertype: intertype,
variant: variant,
type: type,
- params: parseParamTokens(segment[2].item.tokens)
+ params: parseParamTokens(segment[2].tokens)
};
Types.needAnalysis[ret.type] = 0;
ret.ident = toNiceIdent(ret.params[0].ident || 'NOIDENT');
@@ -704,15 +781,37 @@ 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, floatConversion) {
- // We need to min here, since our input might be a double, and large values are rounded, so they can
+ // general idea:
+ //
+ // $1$0 = ~~$d >>> 0;
+ // $1$1 = Math_abs($d) >= 1 ? (
+ // $d > 0 ? Math.min(Math_floor(($d)/ 4294967296.0), 4294967295.0)
+ // : Math_ceil(Math.min(-4294967296.0, $d - $1$0)/ 4294967296.0)
+ // ) : 0;
+ //
+ // We need to min on positive values 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.
+ //
+ // For negatives, we need to ensure a -1 if the value is overall negative, even if not significant negative component
+
var lowInput = legalizedI64s ? value : 'VALUE';
if (floatConversion && ASM_JS) lowInput = asmFloatToInt(lowInput);
+ var low = lowInput + '>>>0';
+ var high = makeInlineCalculation(
+ asmCoercion('Math_abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' +
+ '(VALUE > ' + asmEnsureFloat('0', 'double') + ' ? ' +
+ asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'double') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'double') + ')', 'i32') + '>>>0' +
+ ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'double') + ')', 'double')) + '>>>0' +
+ ')' +
+ ' : 0',
+ value,
+ 'tempDouble'
+ );
if (legalizedI64s) {
- return [lowInput + '>>>0', 'Math.min(Math.floor((' + value + ')/' + asmEnsureFloat(4294967296, 'float') + '), ' + asmEnsureFloat(4294967295, 'float') + ')>>>0'];
+ return [low, high];
} else {
- return makeInlineCalculation(makeI64(lowInput + '>>>0', 'Math.min(Math.floor(VALUE/' + asmEnsureFloat(4294967296, 'float') + '), ' + asmEnsureFloat(4294967295, 'float') + ')>>>0'), value, 'tempBigIntP');
+ return makeI64(low, high);
}
}
function mergeI64(value, unsigned) {
@@ -851,13 +950,13 @@ function parseI64Constant(str, legalized) {
}
function parseNumerical(value, type) {
- if ((!type || type == 'double' || type == 'float') && (value.substr && value.substr(0,2) == '0x')) {
+ if ((!type || type === 'double' || type === 'float') && /^0x/.test(value)) {
// Hexadecimal double value, as the llvm docs say,
// "The one non-intuitive notation for constants is the hexadecimal form of floating point constants."
value = IEEEUnHex(value);
} else if (USE_TYPED_ARRAYS == 2 && isIllegalType(type)) {
return value; // do not parseFloat etc., that can lead to loss of precision
- } else if (value == 'null') {
+ } else if (value === 'null') {
// NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.)
value = '0';
} else if (value === 'true') {
@@ -867,7 +966,15 @@ function parseNumerical(value, type) {
}
if (isNumber(value)) {
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
+ // type may be undefined here, like when this is called from makeConst with a single argument.
+ // but if it is a number, then we can safely assume that this should handle negative zeros
+ // correctly.
+ if (type === undefined || type === 'double' || type === 'float') {
+ if (value[0] === '-' && ret === 0) { return '-.0'; } // fix negative 0, toString makes it 0
+ }
+ if (type === 'double' || type === 'float') {
+ if (!RUNNING_JS_OPTS) ret = asmEnsureFloat(ret, type);
+ }
return ret.toString();
} else {
return value;
@@ -880,18 +987,24 @@ function parseLLVMString(str) {
var ret = [];
var i = 0;
while (i < str.length) {
- var chr = str[i];
- if (chr != '\\') {
- ret.push(chr.charCodeAt(0));
+ var chr = str.charCodeAt(i);
+ if (chr !== 92) { // 92 === '//'.charCodeAt(0)
+ ret.push(chr);
i++;
} else {
- ret.push(eval('0x' + str[i+1]+str[i+2]));
+ ret.push(parseInt(str[i+1]+str[i+2], '16'));
i += 3;
}
}
return ret;
}
+function expandLLVMString(str) {
+ return str.replace(/\\../g, function(m) {
+ return String.fromCharCode(parseInt(m.substr(1), '16'));
+ });
+}
+
function getLabelIds(labels) {
return labels.map(function(label) { return label.ident });
}
@@ -910,11 +1023,9 @@ function getOldLabel(label) {
}
function calcAllocatedSize(type) {
- if (pointingLevels(type) == 0 && isStructType(type)) {
- return Types.types[type].flatSize; // makeEmptyStruct(item.allocatedType).length;
- } else {
- return Runtime.getNativeTypeSize(type); // We can really get away with '1', though, at least on the stack...
- }
+ var ret = Runtime.getNativeTypeSize(type);
+ if (ret) return ret;
+ return Types.types[type].flatSize; // known type
}
// Generates the type signature for a structure, for each byte, the type that is there.
@@ -934,9 +1045,11 @@ function generateStructTypes(type) {
var ret = new Array(size);
var index = 0;
function add(typeData) {
+ var array = typeData.name_[0] === '['; // arrays just have 2 elements in their fields, see calculateStructAlignment
+ var num = array ? parseInt(typeData.name_.substr(1)) : typeData.fields.length;
var start = index;
- for (var i = 0; i < typeData.fields.length; i++) {
- var type = typeData.fields[i];
+ for (var i = 0; i < num; i++) {
+ var type = array ? typeData.fields[0] : typeData.fields[i];
if (!SAFE_HEAP && isPointerType(type)) type = '*'; // do not include unneeded type names without safe heap
if (Runtime.isNumberType(type) || isPointerType(type)) {
if (USE_TYPED_ARRAYS == 2 && type == 'i64') {
@@ -952,9 +1065,17 @@ function generateStructTypes(type) {
}
ret[index++] = type;
} else {
+ if (Runtime.isStructType(type) && type[1] === '0') {
+ // this is [0 x something], which does nothing
+ // XXX this happens in java_nbody... assert(i === typeData.fields.length-1);
+ continue;
+ }
add(Types.types[type]);
}
- var more = (i+1 < typeData.fields.length ? typeData.flatIndexes[i+1] : typeData.flatSize) - (index - start);
+ var more = array ? (i+1)*typeData.flatSize/num : (
+ (i+1 < typeData.fields.length ? typeData.flatIndexes[i+1] : typeData.flatSize)
+ );
+ more -= index - start;
for (var j = 0; j < more; j++) {
ret[index++] = 0;
}
@@ -1049,7 +1170,7 @@ function getHeapOffset(offset, type, forceAsm) {
offset = '(' + offset + ')';
if (shifts != 0) {
if (CHECK_HEAP_ALIGN) {
- return '(CHECK_ALIGN_' + sz + '(' + offset + '|0)>>' + shifts + ')';
+ return '((CHECK_ALIGN_' + sz + '(' + offset + '|0)|0)>>' + shifts + ')';
} else {
return '(' + offset + '>>' + shifts + ')';
}
@@ -1064,19 +1185,37 @@ function makeVarDef(js) {
return js;
}
+function ensureDot(value) {
+ value = value.toString();
+ // if already dotted, or Infinity or NaN, nothing to do here
+ // if smaller than 1 and running js opts, we always need to force a coercion (0.001 will turn into 1e-3, which has no .)
+ if ((value.indexOf('.') >= 0 || /[IN]/.test(value)) && (!RUNNING_JS_OPTS || Math.abs(value) >= 1)) return value;
+ if (RUNNING_JS_OPTS) return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
+ var e = value.indexOf('e');
+ if (e < 0) return value + '.0';
+ return value.substr(0, e) + '.0' + value.substr(e);
+}
+
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;
- // 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 + '))';
+ if (!isNumber(value)) return value;
+ if (PRECISE_F32 && type === 'float') {
+ // normally ok to just emit Math_fround(0), but if the constant is large we may need a .0 (if it can't fit in an int)
+ if (value == 0) return 'Math_fround(0)';
+ value = ensureDot(value);
+ return 'Math_fround(' + value + ')';
+ }
+ if (type in Runtime.FLOAT_TYPES) {
+ return ensureDot(value);
} else {
return value;
}
}
-function asmInitializer(type, impl) {
+function asmInitializer(type) {
if (type in Runtime.FLOAT_TYPES) {
- return '+0';
+ if (PRECISE_F32 && type === 'float') return 'Math_fround(0)';
+ return RUNNING_JS_OPTS ? '+0' : '.0';
} else {
return '0';
}
@@ -1097,7 +1236,11 @@ function asmCoercion(value, type, signedness) {
value = '(' + value + ')|0';
}
}
- return '(+(' + value + '))';
+ if (PRECISE_F32 && type === 'float') {
+ return 'Math_fround(' + value + ')';
+ } else {
+ return '(+(' + value + '))';
+ }
}
} else {
return '((' + value + ')|0)';
@@ -1139,7 +1282,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
var typeData = Types.types[type];
var ret = [];
for (var i = 0; i < typeData.fields.length; i++) {
- ret.push('f' + i + ': ' + makeGetValue(ptr, pos + typeData.flatIndexes[i], typeData.fields[i], noNeedFirst, unsigned));
+ ret.push('f' + i + ': ' + makeGetValue(ptr, pos + typeData.flatIndexes[i], typeData.fields[i], noNeedFirst, unsigned, 0, 0, noSafe));
}
return '{ ' + ret.join(', ') + ' }';
}
@@ -1147,8 +1290,8 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
// 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)) + ',' +
+ return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align, noSafe)) + ',' +
+ makeSetTempDouble(1, 'i32', makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align, noSafe)) + ',' +
makeGetTempDouble(0, 'double') + ')';
}
@@ -1161,12 +1304,12 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
if (isIntImplemented(type)) {
if (bytes == 4 && align == 2) {
// Special case that we can optimize
- ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '|' +
- '(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore) + '<<16)';
+ ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore, 2, noSafe) + '|' +
+ '(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore, 2, noSafe) + '<<16)';
} 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) : '') + ')';
+ ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore, 1, noSafe) + (i > 0 ? '<<' + (8*i) : '') + ')';
if (i < bytes-1) ret += '|';
}
ret = '(' + makeSignOp(ret, type, unsigned ? 'un' : 're', true);
@@ -1188,18 +1331,22 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
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, 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'));
+ if (ASM_JS) {
+ if (!ignore && phase !== 'funcs') return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ', ' + (!!unsigned+0) + ')', type);
+ // else fall through
+ } else {
+ return asmCoercion('SAFE_HEAP_LOAD(' + offset + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type);
}
- return ret;
}
+ 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) {
@@ -1212,15 +1359,11 @@ function indexizeFunctions(value, type) {
var out = {};
if (type && isFunctionType(type, out) && value[0] === '_') { // checking for _ differentiates from $ (local vars)
// add signature to library functions that we now know need indexing
- if (!(value in Functions.implementedFunctions) && !(value in Functions.unimplementedFunctions)) {
- Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : []);
- }
-
- if (BUILD_AS_SHARED_LIB) {
- return '(FUNCTION_TABLE_OFFSET + ' + Functions.getIndex(value) + ')';
- } else {
- return Functions.getIndex(value);
+ var sig = Functions.implementedFunctions[value] || Functions.unimplementedFunctions[value];
+ if (!sig) {
+ sig = Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : [], isVarArgsFunctionType(type));
}
+ return Functions.getIndex(value, sig);
}
return value;
}
@@ -1249,7 +1392,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
value = range(typeData.fields.length).map(function(i) { return value + '.f' + i });
}
for (var i = 0; i < typeData.fields.length; i++) {
- ret.push(makeSetValue(ptr, getFastValue(pos, '+', typeData.flatIndexes[i]), value[i], typeData.fields[i], noNeedFirst));
+ ret.push(makeSetValue(ptr, getFastValue(pos, '+', typeData.flatIndexes[i]), value[i], typeData.fields[i], noNeedFirst, 0, 0, noSafe));
}
return ret.join('; ');
}
@@ -1276,17 +1419,17 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
if (bytes == 4 && align == 2) {
// Special case that we can optimize
ret += 'tempBigInt=' + value + sep;
- ret += makeSetValue(ptr, pos, 'tempBigInt&0xffff', 'i16', noNeedFirst, ignore, 2) + sep;
- ret += makeSetValue(ptr, getFastValue(pos, '+', 2), 'tempBigInt>>16', 'i16', noNeedFirst, ignore, 2);
+ ret += makeSetValue(ptr, pos, 'tempBigInt&0xffff', 'i16', noNeedFirst, ignore, 2, noSafe) + sep;
+ ret += makeSetValue(ptr, getFastValue(pos, '+', 2), 'tempBigInt>>16', 'i16', noNeedFirst, ignore, 2, noSafe);
} else {
ret += 'tempBigInt=' + value + sep;
for (var i = 0; i < bytes; i++) {
- ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore, 1);
+ ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore, 1, noSafe);
if (i < bytes-1) ret += sep + 'tempBigInt = tempBigInt>>8' + sep;
}
}
} else {
- ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8, null, null, true) + sep;
+ ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8, noSafe, null, true) + sep;
ret += makeCopyValues(getFastValue(ptr, '+', pos), 'tempDoublePtr', Runtime.getNativeTypeSize(type), type, null, align, sep);
}
return ret;
@@ -1295,14 +1438,19 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
value = indexizeFunctions(value, type);
var offset = calcFastOffset(ptr, pos, noNeedFirst);
+ if (phase === 'pre' && isNumber(offset)) offset += ' '; // avoid pure numeric strings, seem to be perf issues with overly-aggressive interning or slt in pre processing of heap inits
if (SAFE_HEAP && !noSafe) {
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, forceAsm) + ']=' + value }).join(sep);
+ if (ASM_JS) {
+ if (!ignore && phase !== 'funcs') return asmCoercion('SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type);
+ // else fall through
+ } else {
+ return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
+ }
}
+ return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep);
}
function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
@@ -1329,7 +1477,7 @@ function makeSetValues(ptr, pos, value, type, num, align) {
// 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) || (parseInt(num)/align >= UNROLL_LOOP_MAX)) {
- return '_memset(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + ', ' + asmCoercion(value, 'i32') + ', ' + asmCoercion(num, 'i32') + ')';
+ return '_memset(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + ', ' + asmCoercion(value, 'i32') + ', ' + asmCoercion(num, 'i32') + ')|0';
}
num = parseInt(num);
value = parseInt(value);
@@ -1383,7 +1531,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) {
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 + ')';
+ return '(_memcpy(' + dest + ', ' + src + ', ' + num + ')|0)';
}
num = parseInt(num);
if (ASM_JS) {
@@ -1411,77 +1559,97 @@ function makeHEAPView(which, start, end) {
return 'HEAP' + which + '.subarray((' + start + ')' + mod + ',(' + end + ')' + mod + ')';
}
-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 {
- 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();
+ a = a === 'true' ? '1' : (a === 'false' ? '0' : a);
+ b = b === 'true' ? '1' : (b === 'false' ? '0' : b);
+
+ var aNumber = null, bNumber = null;
+ if (typeof a === 'number') {
+ aNumber = a;
+ a = a.toString();
+ } else if (isNumber(a)) aNumber = parseFloat(a);
+ if (typeof b === 'number') {
+ bNumber = b;
+ b = b.toString();
+ } else if (isNumber(b)) bNumber = parseFloat(b);
+
+ if (aNumber !== null && bNumber !== null) {
+ switch (op) {
+ case '+': return (aNumber + bNumber).toString();
+ case '-': return (aNumber - bNumber).toString();
+ case '*': return (aNumber * bNumber).toString();
+ case '/': {
+ if (type[0] === 'i') {
+ return ((aNumber / bNumber)|0).toString();
+ } else {
+ return (aNumber / bNumber).toString();
+ }
+ }
+ case '%': return (aNumber % bNumber).toString();
+ case '|': return (aNumber | bNumber).toString();
+ case '>>>': return (aNumber >>> bNumber).toString();
+ case '&': return (aNumber & bNumber).toString();
+ case 'pow': return Math.pow(aNumber, bNumber).toString();
+ default: throw 'need to implement getFastValue pn ' + op;
}
}
- if (op == 'pow') {
- if (a == '2' && isIntImplemented(type)) {
+ if (op === 'pow') {
+ if (a === '2' && isIntImplemented(type)) {
return '(1 << (' + b + '))';
}
- return 'Math.pow(' + a + ', ' + b + ')';
+ return 'Math_pow(' + a + ', ' + b + ')';
}
- if (op in PLUS_MUL && isNumber(a)) { // if one of them is a number, keep it last
+ if ((op === '+' || op === '*') && aNumber !== null) { // if one of them is a number, keep it last
var c = b;
b = a;
a = c;
- }
- if (op in MUL_DIV) {
- if (op == '*') {
- if (a == 0 || b == 0) {
- return '0';
- } else if (a == 1) {
- return b;
- } else if (b == 1) {
- return a;
- } else if (isNumber(b) && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) {
- var shifts = Math.log(parseFloat(b))/Math.LN2;
- if (shifts % 1 == 0) {
- return '(' + a + '<<' + shifts + ')';
- }
+ var cNumber = bNumber;
+ bNumber = aNumber;
+ aNumber = cNumber;
+ }
+ if (op === '*') {
+ // We can't eliminate where a or b are 0 as that would break things for creating
+ // a negative 0.
+ if ((aNumber === 0 || bNumber === 0) && !(type in Runtime.FLOAT_TYPES)) {
+ return '0';
+ } else if (aNumber === 1) {
+ return b;
+ } else if (bNumber === 1) {
+ return a;
+ } else if (bNumber !== null && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) {
+ var shifts = Math.log(bNumber)/Math.LN2;
+ if (shifts % 1 === 0) {
+ return '(' + a + '<<' + shifts + ')';
}
- if (!(type in Runtime.FLOAT_TY