blob: 5f5fd08e6e2a9ab724bf093dd9e8bd4c26d125fd [file] [log] [blame]
// Copyright 2010 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
//
// Various tools for parsing LLVM. Utilities of various sorts, that are
// specific to Emscripten (and hence not in utility.js).
//"use strict";
// 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 !== 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>)
// Param filenameHint can be passed as a description to identify the file that is being processed, used
// to locate errors for reporting.
function preprocess(text, filenameHint) {
var lines = text.split('\n');
var ret = '';
var showStack = [];
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
try {
if (line[line.length-1] == '\r') {
line = line.substr(0, line.length-1); // Windows will have '\r' left over from splitting over '\r\n'
}
if (!line[0] || line[0] != '#') {
if (showStack.indexOf(false) == -1) {
ret += line + '\n';
}
} else {
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 (typeof value === 'string') {
// when writing
// #if option == 'stringValue'
// we need to get rid of the quotes
if (value[0] === '"' || value[0] === "'") {
assert(value[value.length - 1] == '"' || value[value.length - 1] == "'");
value = value.substring(1, value.length - 1);
}
}
if (op) {
if (op === '==') {
showStack.push(ident in this && this[ident] == value);
} else if (op === '!=') {
showStack.push(!(ident in this && this[ident] == value));
} else if (op === '<') {
showStack.push(ident in this && this[ident] < value);
} else if (op === '<=') {
showStack.push(ident in this && this[ident] <= value);
} else if (op === '>') {
showStack.push(ident in this && this[ident] > value);
} else if (op === '>=') {
showStack.push(ident in this && this[ident] >= value);
} else {
error('unsupported preprocessor op ' + op);
}
} else {
// Check if a value is truthy.
var short = ident[0] === '!' ? ident.substr(1) : ident;
var truthy = short in this;
if (truthy) {
truthy = !!this[short];
}
if (ident[0] === '!') {
showStack.push(!truthy);
} else {
showStack.push(truthy);
}
}
} else if (line[2] == 'n') { // include
var filename = line.substr(line.indexOf(' ')+1);
if (filename.indexOf('"') === 0) {
filename = filename.substr(1, filename.length - 2);
}
var included = read(filename);
ret += '\n' + preprocess(included, filename) + '\n'
}
} else if (line[2] == 'l') { // else
assert(showStack.length > 0);
showStack.push(!showStack.pop());
} else if (line[2] == 'n') { // endif
assert(showStack.length > 0);
showStack.pop();
} else {
throw "Unclear preprocessor command: " + line;
}
}
} catch(e) {
printErr('parseTools.js preprocessor error in ' + filenameHint + ':' + (i+1) + ': \"' + line + '\"!');
throw e;
}
}
assert(showStack.length == 0);
return ret;
}
function removePointing(type, num) {
if (num === 0) return type;
assert(type.substr(type.length-(num ? num : 1)).replace(/\*/g, '') === ''); //, 'Error in removePointing with ' + [type, num, type.substr(type.length-(num ? num : 1))]);
return type.substr(0, type.length-(num ? num : 1));
}
function pointingLevels(type) {
if (!type) return 0;
var ret = 0;
var len1 = type.length - 1;
while (type[len1-ret] && type[len1-ret] === '*') {
ret++;
}
return ret;
}
function removeAllPointing(type) {
return removePointing(type, pointingLevels(type));
}
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, '_');
}
// Kind of a hack. In some cases we have strings that we do not want
// to |toNiceIdent|, as they are the output of previous processing. We
// should refactor everything into an object, with an explicit flag
// saying what has been |toNiceIdent|ed. Until then, this will detect
// simple idents that are in need of |toNiceIdent|ation. Or, we should
// ensure that processed strings never start with %,@, e.g. by always
// enclosing them in ().
function toNiceIdentCarefully(ident) {
if (ident[0] == '%' || ident[0] == '@') ident = toNiceIdent(ident);
return ident;
}
// Returns true if ident is a niceIdent (see toNiceIdent). If loose
// is true, then also allow () and spaces.
function isNiceIdent(ident, loose) {
if (loose) {
return /^\(?[$_]+[\w$_\d ]*\)?$/.test(ident);
} else {
return /^[$_]+[\w$_\d]*$/.test(ident);
}
}
// 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) {
// This test is necessary for clang - in llvm-gcc, we
// could check for %struct. The downside is that %1 can
// be either a variable or a structure, and we guess it is
// a struct, which can lead to |call i32 %5()| having
// |%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 happens in isStructType()
return !Compiletime.isNumberType(type) && type[0] == '%';
}
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 (isArrayType(type)) return true;
if (/<?\{ ?[^}]* ?\}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types
// See comment in isStructPointerType()
return type[0] == '%';
}
function isVectorType(type) {
return type[type.length-1] === '>';
}
function isStructuralType(type) {
return /^\{ ?[^}]* ?\}$/.test(type); // { i32, i8 } etc. - anonymous struct types
}
function getStructuralTypeParts(type) { // split { i32, i8 } etc. into parts
return type.replace(/[ {}]/g, '').split(',');
}
function getStructuralTypePartBits(part) {
return Math.ceil((getBits(part) || 32)/32)*32; // simple 32-bit alignment. || 32 is for pointers
}
function isIntImplemented(type) {
return type[0] == 'i' || isPointerType(type);
}
// Note: works for iX types and structure types, not pointers (even though they are implemented as ints)
function getBits(type, allowPointers) {
if (allowPointers && isPointerType(type)) return 32;
if (!type) return 0;
if (type[0] == 'i') {
var left = type.substr(1);
if (!isNumber(left)) return 0;
return parseInt(left);
}
if (isStructuralType(type)) {
return sum(getStructuralTypeParts(type).map(getStructuralTypePartBits));
}
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 Compiletime.FLOAT_TYPES) + +(type2 in Compiletime.FLOAT_TYPES);
if (floats == 2) return true;
if (floats == 1) return false;
return getNumIntChunks(type1) == getNumIntChunks(type2);
}
function isIllegalType(type) {
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) {
return type == 'void';
}
// Detects a function definition, ([...|type,[type,...]])
function isFunctionDef(token, out) {
var text = token.text;
var nonPointing = removeAllPointing(text);
if (nonPointing[0] != '(' || nonPointing.substr(-1) != ')')
return false;
if (nonPointing === '()') return true;
if (!token.tokens) return false;
var fail = false;
var segments = splitTokenList(token.tokens);
segments.forEach(function(segment) {
var subtext = segment[0].text;
fail = fail || segment.length > 1 || !(isType(subtext) || subtext == '...');
});
if (out) {
out.segments = segments;
out.numArgs = segments.length;
}
return !fail;
}
function isPossiblyFunctionType(type) {
// A quick but unreliable way to see if something is a function type. Yes is just 'maybe', no is definite.
var len = type.length;
return type[len-2] == ')' && type[len-1] == '*';
}
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 lastOpen = type.lastIndexOf('(');
var returnType;
if (firstOpen == lastOpen) {
returnType = getReturnType(type);
if (!isType(returnType)) return false;
} else {
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--;
}
assert(argText);
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;
}
var isTypeCache = {}; // quite hot, optimize as much as possible
function isType(type) {
if (type in isTypeCache) return isTypeCache[type];
var ret = isPointerType(type) || isVoidType(type) || Compiletime.isNumberType(type) || isStructType(type) || isFunctionType(type);
isTypeCache[type] = ret;
return ret;
}
var SPLIT_TOKEN_LIST_SPLITTERS = set(',', 'to'); // 'to' can separate parameters as well...
// Splits a list of tokens separated by commas. For example, a list of arguments in a function call
function splitTokenList(tokens) {
if (tokens.length == 0) return [];
if (!tokens.slice) tokens = tokens.tokens;
var ret = [];
var seg = [];
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (token.text in SPLIT_TOKEN_LIST_SPLITTERS) {
ret.push(seg);
seg = [];
} else if (token.text == ';') {
ret.push(seg);
return ret;
} else {
seg.push(token);
}
}
if (seg.length) ret.push(seg);
return ret;
}
var UNINDEXABLE_GLOBALS = set(
'_llvm_global_ctors' // special-cased
);
function isIndexableGlobal(ident) {
if (!(ident in Variables.globals)) return false;
if (ident in UNINDEXABLE_GLOBALS) {
Variables.globals[ident].unIndexable = true;
return false;
}
var data = Variables.globals[ident];
return !data.alias && !data.external;
}
function makeGlobalUse(ident) {
if (isIndexableGlobal(ident)) {
var index = Variables.indexedGlobals[ident];
if (index === undefined) {
// we are accessing this before we index globals, likely from the library. mark as unindexable
UNINDEXABLE_GLOBALS[ident] = 1;
return ident;
}
var ret = (Runtime.GLOBAL_BASE + index).toString();
if (SIDE_MODULE) ret = '(H_BASE+' + ret + ')';
return ret;
}
return ident;
}
function _IntToHex(x) {
assert(x >= 0 && x <= 15);
if (x <= 9) {
return String.fromCharCode('0'.charCodeAt(0) + x);
} else {
return String.fromCharCode('A'.charCodeAt(0) + x - 10);
}
}
function IEEEUnHex(stringy) {
stringy = stringy.substr(2); // leading '0x';
if (stringy.replace(/0/g, '') === '') return 0;
while (stringy.length < 16) stringy = '0' + stringy;
assert(stringy.length === 16, 'Can only unhex 16-digit double numbers, nothing platform-specific'); // |long double| might cause this
var top = eval('0x' + stringy[0]);
var neg = !!(top & 8); // sign
if (neg) {
stringy = _IntToHex(top & ~8) + stringy.substr(1);
}
var a = eval('0x' + stringy.substr(0, 8)); // top half
var b = eval('0x' + stringy.substr(8)); // bottom half
var e = a >> ((52 - 32) & 0x7ff); // exponent
a = a & 0xfffff;
if (e === 0x7ff) {
if (a == 0 && b == 0) {
return neg ? '-Infinity' : 'Infinity';
} else {
return 'NaN';
}
}
e -= 1023; // offset
var absolute = ((((a | 0x100000) * 1.0) / Math.pow(2,52-32)) * Math.pow(2, e)) + (((b * 1.0) / Math.pow(2, 52)) * Math.pow(2, e));
return (absolute * (neg ? -1 : 1)).toString();
}
// Given an expression like (VALUE=VALUE*2,VALUE<10?VALUE:t+1) , this will
// replace VALUE with value. If value is not a simple identifier of a variable,
// value will be replaced with tempVar.
function makeInlineCalculation(expression, value, tempVar) {
if (!isNiceIdent(value, true)) {
expression = tempVar + '=' + value + ',' + expression;
value = tempVar;
}
return '(' + expression.replace(/VALUE/g, value) + ')';
}
// Makes a proper runtime value for a 64-bit value from low and high i32s. low and high are assumed to be unsigned.
function makeI64(low, high) {
high = high || '0';
return '[' + makeSignOp(low, 'i32', 'un', 1, 1) + ',' + makeSignOp(high, 'i32', 'un', 1, 1) + ']';
}
// XXX Make all i64 parts signed
// Splits a number (an integer in a double, possibly > 32 bits) into an i64 value, represented by a low and high i32 pair.
// Will suffer from rounding. mergeI64 does the opposite.
function splitI64(value, floatConversion) {
// 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) 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 [low, high];
} else {
return makeI64(low, high);
}
}
function mergeI64(value, unsigned) {
if (legalizedI64s) {
return RuntimeGenerator.makeBigInt(value + '$0', value + '$1', unsigned);
} else {
return makeInlineCalculation(RuntimeGenerator.makeBigInt('VALUE[0]', 'VALUE[1]', unsigned), value, 'tempI64');
}
}
// Takes an i64 value and changes it into the [low, high] form used in i64 mode 1. In that
// mode, this is a no-op
function ensureI64_1(value) {
return value;
}
function makeCopyI64(value) {
return value + '.slice(0)';
}
// Given a string representation of an integer of arbitrary size, return it
// split up into 32-bit chunks
function parseArbitraryInt(str, bits) {
// We parse the string into a vector of digits, base 10. This is convenient to work on.
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 = [];
for (var i = 0; i < s.length; i++) {
ret.push(s.charCodeAt(i) - '0'.charCodeAt(0));
}
return ret;
}
function divide2(v) { // v /= 2
for (var i = v.length-1; i >= 0; i--) {
var d = v[i];
var r = d % 2;
d = Math.floor(d/2);
v[i] = d;
if (r) {
assert(i+1 < v.length);
var d2 = v[i+1];
d2 += 5;
if (d2 >= 10) {
v[i] = d+1;
d2 -= 10;
}
v[i+1] = d2;
}
}
}
function mul2(v) { // v *= 2
for (var i = v.length-1; i >= 0; i--) {
var d = v[i]*2;
r = d >= 10;
v[i] = d%10;
var j = i-1;
if (r) {
if (j < 0) {
v.unshift(1);
break;
}
v[j] += 0.5; // will be multiplied
}
}
}
function subtract(v, w) { // v -= w. we assume v >= w
while (v.length > w.length) w.splice(0, 0, 0);
for (var i = 0; i < v.length; i++) {
v[i] -= w[i];
if (v[i] < 0) {
v[i] += 10;
// find something to take from
var j = i-1;
while (v[j] == 0) {
v[j] = 9;
j--;
assert(j >= 0);
}
v[j]--;
}
}
}
function isZero(v) {
for (var i = 0; i < v.length; i++) {
if (v[i] > 0) return false;
}
return true;
}
var v;
if (str[0] == '-') {
// twos-complement is needed
str = str.substr(1);
v = str2vec('1');
for (var i = 0; i < bits; i++) {
mul2(v);
}
subtract(v, str2vec(str));
} else {
v = str2vec(str);
}
var bitsv = [];
while (!isZero(v)) {
bitsv.push((v[v.length-1] % 2 != 0)+0);
v[v.length-1] = v[v.length-1] & 0xfe;
divide2(v);
}
var ret = zeros(Math.ceil(bits/32));
for (var i = 0; i < bitsv.length; i++) {
ret[Math.floor(i/32)] += bitsv[i]*Math.pow(2, i % 32);
}
return ret;
}
function parseI64Constant(str, legalized) {
if (!isNumber(str)) {
// This is a variable. Copy it, so we do not modify the original
return legalizedI64s ? str : makeCopyI64(str);
}
var parsed = parseArbitraryInt(str, 64);
if (legalizedI64s || legalized) return parsed;
return '[' + parsed[0] + ',' + parsed[1] + ']';
}
function parseNumerical(value, type) {
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 (isIllegalType(type)) {
return value; // do not parseFloat etc., that can lead to loss of precision
} else if (value === 'null') {
// NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.)
value = '0';
} else if (value === 'true') {
return '1';
} else if (value === 'false') {
return '0';
}
if (isNumber(value)) {
var ret = parseFloat(value); // will change e.g. 5.000000e+01 to 50
// 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;
}
}
// \0Dsometext is really '\r', then sometext
// This function returns an array of int values
function parseLLVMString(str) {
var ret = [];
var i = 0;
while (i < str.length) {
var chr = str.charCodeAt(i);
if (chr !== 92) { // 92 === '//'.charCodeAt(0)
ret.push(chr);
i++;
} else {
ret.push(parseInt(str[i+1]+str[i+2], '16'));
i += 3;
}
}
return ret;
}
// Generates the type signature for a structure, for each byte, the type that is there.
// i32, 0, 0, 0 - for example, an int32 is here, then nothing to do for the 3 next bytes, naturally
function generateStructTypes(type) {
if (isArray(type)) return type; // already in the form of [type, type,...]
if (Compiletime.isNumberType(type) || isPointerType(type)) {
if (type == 'i64') {
return ['i64', 0, 0, 0, 'i32', 0, 0, 0];
}
return [type].concat(zeros(Runtime.getNativeFieldSize(type)-1));
}
// Avoid multiple concats by finding the size first. This is much faster
var typeData = Types.types[type];
var size = typeData.flatSize;
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 < 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 (Compiletime.isNumberType(type) || isPointerType(type)) {
if (type == 'i64') {
ret[index++] = 'i64';
ret[index++] = 0;
ret[index++] = 0;
ret[index++] = 0;
ret[index++] = 'i32';
ret[index++] = 0;
ret[index++] = 0;
ret[index++] = 0;
continue;
}
ret[index++] = type;
} else {
if (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 = 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;
}
}
}
add(typeData);
assert(index == size);
return ret;
}
// Misc
function indentify(text, indent) {
if (text.length > 1024*1024) return text; // Don't try to indentify huge strings - we may run out of memory
if (typeof indent === 'number') {
var len = indent;
indent = '';
for (var i = 0; i < len; i++) indent += ' ';
}
return text.replace(/\n/g, '\n' + indent);
}
// Correction tools
function checkSafeHeap() {
return SAFE_HEAP === 1;
}
function getHeapOffset(offset, type) {
if (Runtime.getNativeFieldSize(type) > 4) {
if (type == 'i64') {
type = 'i32'; // we emulate 64-bit integer values as 32 in asmjs-unknown-emscripten, but not double
}
}
var sz = Runtime.getNativeTypeSize(type);
var shifts = Math.log(sz)/Math.LN2;
offset = '(' + offset + ')';
return '(' + offset + '>>' + shifts + ')';
}
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 (!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 Compiletime.FLOAT_TYPES) {
return ensureDot(value);
} else {
return value;
}
}
function asmInitializer(type) {
if (type in Compiletime.FLOAT_TYPES) {
if (PRECISE_F32 && type === 'float') return 'Math_fround(0)';
return RUNNING_JS_OPTS ? '+0' : '.0';
} else {
return '0';
}
}
function asmCoercion(value, type, signedness) {
if (type == 'void') {
return value;
} else if (type in Compiletime.FLOAT_TYPES) {
if (isNumber(value)) {
return asmEnsureFloat(value, type);
} else {
if (signedness) {
if (signedness == 'u') {
value = '(' + value + ')>>>0';
} else {
value = '(' + value + ')|0';
}
}
if (PRECISE_F32 && type === 'float') {
return 'Math_fround(' + value + ')';
} else {
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 {
offset = getHeapOffset(ptr, type);
}
var ret = slab + '[' + offset + ']';
if (!forSet) ret = asmCoercion(ret, type);
return ret;
}
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, 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 = [];
for (var i = 0; i < typeData.fields.length; i++) {
ret.push('f' + i + ': ' + makeGetValue(ptr, pos + typeData.flatIndexes[i], typeData.fields[i], noNeedFirst, unsigned, 0, 0, noSafe));
}
return '{ ' + ret.join(', ') + ' }';
}
// In double mode 1, in asmjs-unknown-emscripten we need this code path if we are not fully aligned.
if (DOUBLE_MODE == 1 && type == 'double' && (align < 8)) {
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') + ')';
}
if (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, 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, 1, noSafe) + (i > 0 ? '<<' + (8*i) : '') + ')';
if (i < bytes-1) ret += '|';
}
ret = '(' + makeSignOp(ret, type, unsigned ? 'un' : 're', true);
}
} else {
if (type == 'float') {
ret += 'copyTempFloat(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + '),' + makeGetTempDouble(0, 'float');
} else {
ret += 'copyTempDouble(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + '),' + makeGetTempDouble(0, 'double');
}
}
ret += ')';
return ret;
}
}
var offset = calcFastOffset(ptr, pos, noNeedFirst);
if (SAFE_HEAP && !noSafe) {
var printType = type;
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
if (printType[0] === '#') printType = printType.substr(1);
if (!ignore) {
return asmCoercion('SAFE_HEAP_LOAD' + ((type in Compiletime.FLOAT_TYPES) ? '_D' : '') + '(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + (!!unsigned+0) + ')', type);
}
}
var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type) + ']';
if (forceAsm) {
ret = asmCoercion(ret, type);
}
return ret;
}
function makeGetValueAsm(ptr, pos, type, unsigned) {
return makeGetValue(ptr, pos, type, null, unsigned, null, null, null, true);
}
//! @param ptr The pointer. Used to find both the slab and the offset in that slab. If the pointer
//! is just an integer, then this is almost redundant, but in general the pointer type
//! may in the future include information about which slab as well. So, for now it is
//! possible to put |0| here, but if a pointer is available, that is more future-proof.
//! @param pos The position in that slab - the offset. Added to any offset in the pointer itself.
//! @param value The value to set.
//! @param type A string defining the type. Used to find the slab (HEAPU8, HEAP16, HEAPU32, etc.).
//! '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, 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];
var ret = [];
// We can receive either an object - an object literal that was in the .ll - or a string,
// which is the ident of an aggregate struct
if (typeof value === 'string') {
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, 0, 0, noSafe));
}
return ret.join('; ');
}
if (DOUBLE_MODE == 1 && type == 'double' && (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 (type == 'i64') {
return '(tempI64 = [' + splitI64(value) + '],' +
makeSetValue(ptr, pos, 'tempI64[0]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' +
makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempI64[1]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')';
}
var bits = getBits(type);
var needSplitting = bits > 0 && !isPowerOfTwo(bits); // an unnatural type like i24
if (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)) {
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, 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, noSafe);
if (i < bytes-1) ret += sep + 'tempBigInt = tempBigInt>>8' + sep;
}
}
} else {
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;
}
}
var offset = calcFastOffset(ptr, pos, noNeedFirst);
if (SAFE_HEAP && !noSafe) {
var printType = type;
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
if (printType[0] === '#') printType = printType.substr(1);
if (!ignore) {
return 'SAFE_HEAP_STORE' + ((type in Compiletime.FLOAT_TYPES) ? '_D' : '') + '(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + Runtime.getNativeTypeSize(type) + ')';
}
}
return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type) + ']=' + value }).join(sep);
}
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) {
function unroll(type, num, jump, value$) {
jump = jump || 1;
value$ = value$ || value;
return range(num).map(function(i) {
return makeSetValue(ptr, getFastValue(pos, '+', i*jump), value$, type);
}).join('; ');
}
// 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') + ')|0';
}
num = parseInt(num);
value = parseInt(value);
if (value < 0) value += 256; // make it unsigned
var values = {
1: value,
2: value | (value << 8),
4: value | (value << 8) | (value << 16) | (value << 24)
};
var ret = [];
[4, 2, 1].forEach(function(possibleAlign) {
if (num == 0) return;
if (align >= possibleAlign) {
ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign, values[possibleAlign]));
pos = getFastValue(pos, '+', Math.floor(num/possibleAlign)*possibleAlign);
num %= possibleAlign;
}
});
return ret.join('; ');
}
var TYPED_ARRAY_SET_MIN = Infinity; // .set() as memcpy seems to just slow us down
function makeCopyValues(dest, src, num, type, modifier, align, sep) {
sep = sep || ';';
function unroll(type, num, jump) {
jump = jump || 1;
return range(num).map(function(i) {
return makeSetValue(dest, i*jump, makeGetValue(src, i*jump, type), type);
}).join(sep);
}
// 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 memcpy
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 + ')|0)';
}
num = parseInt(num);
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) {
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;
}
});
return ret.join(sep);
}
function makeHEAPView(which, start, end) {
var size = parseInt(which.replace('U', '').replace('F', ''))/8;
var mod = size == 1 ? '' : ('>>' + log2(size));
return 'HEAP' + which + '.subarray((' + start + ')' + mod + ',(' + end + ')' + mod + ')';
}
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 === '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)) {
return '(1 << (' + b + '))';
}
return 'Math_pow(' + a + ', ' + b + ')';
}
if ((op === '+' || op === '*') && aNumber !== null) { // if one of them is a number, keep it last
var c = b;
b = a;
a = c;
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 Compiletime.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 Compiletime.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 ((aNumber !== null && Math.abs(a) < TWO_TWENTY) || (bNumber !== null && 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 + ')|0)';
}
} else if (op === '/') {
if (a === '0' && !(type in Compiletime.FLOAT_TYPES)) { // careful on floats, since 0*NaN is not 0
return '0';
} else if (b === 1) {
return a;
} // Doing shifts for division is problematic, as getting the rounding right on negatives is tricky
} else if (op === '+' || op === '-') {
if (b[0] === '-') {
op = op === '+' ? '-' : '+';
b = b.substr(1);
}
if (aNumber === 0) {
return op === '+' ? b : '(-' + b + ')';
} else if (bNumber === 0) {
return a;
}
}
return '(' + a + ')' + op + '(' + b + ')';
}
function getFastValues(list, op, type) {
assert(op === '+' && type === 'i32');
for (var i = 0; i < list.length; i++) {
if (isNumber(list[i])) list[i] = (list[i]|0) + '';
}
var changed = true;
while (changed) {
changed = false;
for (var i = 0; i < list.length-1; i++) {
var fast = getFastValue(list[i], op, list[i+1], type);
var raw = list[i] + op + list[i+1];
if (fast.length < raw.length || fast.indexOf(op) < 0) {
if (isNumber(fast)) fast = (fast|0) + '';
list[i] = fast;
list.splice(i+1, 1);
i--;
changed = true;
break;
}
}
}
if (list.length == 1) return list[0];
return list.reduce(function(a, b) { return a + op + b });
}
function calcFastOffset(ptr, pos, noNeedFirst) {
assert(!noNeedFirst);
return getFastValue(ptr, '+', pos, 'i32');
}
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 (typeof slab == 'string' && (slab.substr(0, 4) === 'HEAP')) return pos;
var types = generateStructTypes(type);
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;
}
}
}
// compress type info and data if possible
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') {
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
if (SIDE_MODULE && typeof ptr == 'string') {
ptr = parseInt(ptr.substring(ptr.indexOf('+'), ptr.length-1)); // parse into (H_BASE+X)
}
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';
}
if (typeof types == 'object') {
while (types.length < slab.length) types.push(0);
}
types = JSON.stringify(types);
if (typeof slab == 'object') slab = '[' + slab.join(',') + ']';
return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ');';
}
function makeGetSlabs(ptr, type, allowMultiple, unsigned) {
assert(type);
if (isPointerType(type)) type = 'i32'; // Hardcoded 32-bit
switch(type) {
case 'i1': case 'i8': return [unsigned ? 'HEAPU8' : 'HEAP8']; break;
case 'i16': return [unsigned ? 'HEAPU16' : 'HEAP16']; break;
case '<4 x i32>':
case 'i32': case 'i64': return [unsigned ? 'HEAPU32' : 'HEAP32']; break;
case 'double': return ['HEAPF64'];
case '<4 x float>':
case 'float': return ['HEAPF32'];
default: {
throw 'what, exactly, can we do for unknown types in TA2?! ' + [new Error().stack, ptr, type, allowMultiple, unsigned];
}
}
return [];
}
function makeGetTempRet0() {
return "(getTempRet0() | 0)";
}
function makeSetTempRet0(value) {
return "setTempRet0((" + value + ") | 0)";
}
function makeStructuralReturn(values, inAsm) {
var i = -1;
return 'return ' + asmCoercion(values.slice(1).map(function(value) {
i++;
if (!inAsm) {
return 'setTempRet' + i + '(' + value + ')';
}
if (i === 0) {
return makeSetTempRet0(value)
} else {
return 'tempRet' + i + ' = ' + value;
}
}).concat([values[0]]).join(','), 'i32');
}
function makeThrow(what) {
return 'throw ' + what + (DISABLE_EXCEPTION_CATCHING == 1 ? ' + " - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch."' : '') + ';';
}
function makeSignOp(value, type, op, force, ignore) {
if (type == 'i64') {
return value; // these are always assumed to be two 32-bit unsigneds.
}
if (isPointerType(type)) type = 'i32'; // Pointers are treated as 32-bit ints
if (!value) return value;
var bits, full;
if (type[0] === 'i') {
bits = parseInt(type.substr(1));
full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore) + ')';
// Always sign/unsign constants at compile time, regardless of CHECK/CORRECT
if (isNumber(value)) {
return eval(full).toString();
}
}
if ((ignore) && !force) return value;
if (type[0] === 'i') {
// this is an integer, but not a number (or we would have already handled it)
// shortcuts
if (ignore) {
if (value === 'true') {
value = '1';
} else if (value === 'false') {
value = '0';
} else if (needsQuoting(value)) value = '(' + value + ')';
if (bits === 32) {
if (op === 're') {
return '(' + value + '|0)';
} else {
return '(' + value + '>>>0)';
}
} else if (bits < 32) {
if (op === 're') {
return '((' + value + '<<' + (32-bits) + ')>>' + (32-bits) + ')';
} else {
return '(' + value + '&' + (Math.pow(2, bits)-1) + ')';
}
} else { // bits > 32
if (op === 're') {
return makeInlineCalculation('VALUE >= ' + Math.pow(2, bits-1) + ' ? VALUE-' + Math.pow(2, bits) + ' : VALUE', value, 'tempBigIntS');
} else {
return makeInlineCalculation('VALUE >= 0 ? VALUE : ' + Math.pow(2, bits) + '+VALUE', value, 'tempBigIntS');
}
}
}
return full;
}
return value;
}
var legalizedI64s = true; // We do not legalize globals, but do legalize function lines. This will be true in the latter case
function stripCorrections(param) {
var m;
while (true) {
if (m = /^\((.*)\)$/.exec(param)) {
param = m[1];
continue;
}
if (m = /^\(([$_\w]+)\)&\d+$/.exec(param)) {
param = m[1];
continue;
}
if (m = /^\(([$_\w()]+)\)\|0$/.exec(param)) {
param = m[1];
continue;
}
if (m = /^\(([$_\w()]+)\)\>>>0$/.exec(param)) {
param = m[1];
continue;
}
if (m = /CHECK_OVERFLOW\(([^,)]*),.*/.exec(param)) {
param = m[1];
continue;
}
break;
}
return param;
}
function getImplementationType(varInfo) {
if (varInfo.impl == 'nativized') {
return removePointing(varInfo.type);
}
return varInfo.type;
}
function charCode(char) {
return char.charCodeAt(0);
}
function getTypeFromHeap(suffix) {
switch (suffix) {
case '8': return 'i8';
case '16': return 'i16';
case '32': return 'i32';
case 'F32': return 'float';
case 'F64': return 'double';
default: throw 'getTypeFromHeap? ' + suffix;
}
}
function ensureValidFFIType(type) {
return type === 'float' ? 'double' : type; // ffi does not tolerate float XXX
}
// FFI return values must arrive as doubles, and we can force them to floats afterwards
function asmFFICoercion(value, type) {
value = asmCoercion(value, ensureValidFFIType(type));
if (PRECISE_F32 && type === 'float') value = asmCoercion(value, 'float');
return value;
}
function makeDynCall(sig) {
if (!EMULATED_FUNCTION_POINTERS) {
return 'dynCall_' + sig;
} else {
return 'ftCall_' + sig;
}
}
function heapAndOffset(heap, ptr) { // given HEAP8, ptr , we return splitChunk, relptr
return heap + ',' + ptr;
}
function makeEval(code) {
if (DYNAMIC_EXECUTION == 0) {
// Treat eval as error.
return "abort('DYNAMIC_EXECUTION=0 was set, cannot eval');";
}
var ret = '';
if (DYNAMIC_EXECUTION == 2) {
// Warn on evals, but proceed.
ret += "err('Warning: DYNAMIC_EXECUTION=2 was set, but calling eval in the following location:');\n";
ret += "err(stackTrace());\n";
}
ret += code;
return ret;
}
function makeStaticAlloc(size) {
size = (size + (STACK_ALIGN-1)) & -STACK_ALIGN;
return 'STATICTOP; STATICTOP += ' + size + ';';
}
function makeRetainedCompilerSettings() {
var blacklist = set('STRUCT_INFO');
var ret = {};
for (var x in this) {
try {
if (x[0] !== '_' && !(x in blacklist) && x == x.toUpperCase() && (typeof this[x] === 'number' || typeof this[x] === 'string' || this.isArray())) ret[x] = this[x];
} catch(e){}
}
return ret;
}