aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js6
-rw-r--r--src/jsifier.js2
-rw-r--r--src/library.js11
-rw-r--r--src/parseTools.js123
-rw-r--r--src/preamble.js35
-rw-r--r--src/runtime.js10
-rw-r--r--src/settings.js7
7 files changed, 130 insertions, 64 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 7412be6d..a724d229 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -463,6 +463,12 @@ function analyzer(data, sidePass) {
item.functions.forEach(function(func) {
func.lines.forEach(function(line, i) {
if (line.intertype === 'assign' && line.value.intertype === 'load') {
+ // Floats have no concept of signedness. Mark them as 'signed', which is the default, for which we do nothing
+ if (line.value.type in Runtime.FLOAT_TYPES) {
+ line.value.unsigned = false;
+ return;
+ }
+ // Booleans are always unsigned
var data = func.variables[line.ident];
if (data.type === 'i1') {
line.value.unsigned = true;
diff --git a/src/jsifier.js b/src/jsifier.js
index 657a9673..7bff588c 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -71,7 +71,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
} else {
- libFuncsToInclude = ['memset', 'malloc', 'free'];
+ libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free'];
}
libFuncsToInclude.forEach(function(ident) {
data.functionStubs.push({
diff --git a/src/library.js b/src/library.js
index 5a429131..ad1ff696 100644
--- a/src/library.js
+++ b/src/library.js
@@ -2260,7 +2260,6 @@ LibraryManager.library = {
} else if (type == 'i64') {
ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}},
{{{ makeGetValue('varargs', 'argIndex+4', 'i32', undefined, undefined, true) }}}];
- ret = unSign(ret[0], 32) + unSign(ret[1], 32)*Math.pow(2, 32); // Unsigned in this notation. Signed later if needed. // XXX - loss of precision
#else
} else if (type == 'i64') {
ret = {{{ makeGetValue('varargs', 'argIndex', 'i64', undefined, undefined, true) }}};
@@ -2270,7 +2269,7 @@ LibraryManager.library = {
ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}};
}
argIndex += Runtime.getNativeFieldSize(type);
- return Number(ret);
+ return ret;
}
var ret = [];
@@ -2392,6 +2391,12 @@ LibraryManager.library = {
var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0);
argSize = argSize || 4;
var currArg = getNextArg('i' + (argSize * 8));
+#if I64_MODE == 1
+ // Flatten i64-1 [low, high] into a (slightly rounded) double
+ if (argSize == 8) {
+ currArg = Runtime.makeBigInt(currArg[0], currArg[1], next == 'u'.charCodeAt(0));
+ }
+#endif
// Truncate to requested size.
if (argSize <= 4) {
var limit = Math.pow(256, argSize) - 1;
@@ -3444,7 +3449,7 @@ LibraryManager.library = {
},
strtoll__deps: ['_parseInt'],
strtoll: function(str, endptr, base) {
- return __parseInt(str, endptr, base, -9223372036854775808, 9223372036854775807, 64); // LLONG_MIN, LLONG_MAX; imprecise.
+ return __parseInt(str, endptr, base, -9223372036854775200, 9223372036854775200, 64); // LLONG_MIN, LLONG_MAX; imprecise.
},
strtol__deps: ['_parseInt'],
strtol: function(str, endptr, base) {
diff --git a/src/parseTools.js b/src/parseTools.js
index ad6f2830..49e5b411 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -558,22 +558,13 @@ function makeInlineCalculation(expression, value, tempVar) {
return '(' + expression.replace(/VALUE/g, value) + ')';
}
-// Given two 32-bit unsigned parts of an emulated 64-bit number, combine them into a JS number (double).
-// Rounding is inevitable if the number is large. This is a particular problem for small negative numbers
-// (-1 will be rounded!), so handle negatives separately and carefully
-function makeBigInt(low, high) {
- // here VALUE will be the big part
- return '(' + high + ' <= 2147483648 ? (' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 'un', 1, 1) + '*4294967296))' +
- ' : (' + makeSignOp(low, 'i32', 're', 1, 1) + '+(1+' + makeSignOp(high, 'i32', 're', 1, 1) + ')*4294967296))';
-}
-
// 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';
if (I64_MODE == 1) {
return '[' + makeSignOp(low, 'i32', 'un', 1, 1) + ',' + makeSignOp(high, 'i32', 'un', 1, 1) + ']';
} else {
- if (high) return makeBigInt(low, high);
+ if (high) return RuntimeGenerator.makeBigInt(low, high);
return low;
}
}
@@ -589,7 +580,7 @@ function splitI64(value) {
}
function mergeI64(value) {
assert(I64_MODE == 1);
- return makeInlineCalculation(makeBigInt('VALUE[0]', 'VALUE[1]'), value, 'tempI64');
+ return makeInlineCalculation(RuntimeGenerator.makeBigInt('VALUE[0]', 'VALUE[1]'), value, 'tempI64');
}
// Takes an i64 value and changes it into the [low, high] form used in i64 mode 1. In that
@@ -912,23 +903,32 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
'tempDoubleF64[0])';
}
- if (EMULATE_UNALIGNED_ACCESSES && USE_TYPED_ARRAYS == 2 && align && isIntImplemented(type)) { // TODO: support unaligned doubles and floats
+ if (USE_TYPED_ARRAYS == 2 && align) {
// Alignment is important here. May need to split this up
var bytes = Runtime.getNativeTypeSize(type);
if (bytes > align) {
- var ret = '/* unaligned */(';
- if (bytes <= 4) {
- for (var i = 0; i < bytes; i++) {
- ret += 'tempInt' + (i == 0 ? '=' : (i < bytes-1 ? '+=((' : '+(('));
- ret += makeSignOp(makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, unsigned, ignore), 'i8', 'un', true);
- if (i > 0) ret += ')<<' + (8*i) + ')';
- if (i < bytes-1) ret += ',';
+ var ret = '(';
+ if (isIntImplemented(type)) {
+ if (bytes <= 4) {
+ for (var i = 0; i < bytes; i++) {
+ ret += 'tempInt' + (i == 0 ? '=' : '|=((');
+ ret += makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore);
+ if (i > 0) ret += ')<<' + (8*i) + ')';
+ ret += ',';
+ }
+ ret += makeSignOp('tempInt', type, unsigned ? 'un' : 're', true);
+ } else {
+ assert(bytes == 8);
+ ret += 'tempBigInt=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, true, ignore, align) + ',';
+ ret += 'tempBigInt2=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, true, ignore, align) + ',';
+ ret += makeI64('tempBigInt', 'tempBigInt2');
}
} else {
- assert(bytes == 8);
- ret += 'tempBigInt=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, true, ignore, align) + ',';
- ret += 'tempBigInt2=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, true, ignore, align) + ',';
- ret += makeI64('tempBigInt', 'tempBigInt2');
+ if (type == 'float') {
+ ret += 'copyTempFloat(' + getFastValue(ptr, '+', pos) + '),tempDoubleF32[0]';
+ } else {
+ ret += 'copyTempDouble(' + getFastValue(ptr, '+', pos) + '),tempDoubleF64[0]';
+ }
}
ret += ')';
return ret;
@@ -994,22 +994,27 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe)
makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempDoubleI32[1]', 'i32', noNeedFirst, ignore, align) + ')';
}
- if (EMULATE_UNALIGNED_ACCESSES && USE_TYPED_ARRAYS == 2 && align && isIntImplemented(type)) { // TODO: support unaligned doubles and floats
+ if (USE_TYPED_ARRAYS == 2 && align) {
// Alignment is important here. May need to split this up
var bytes = Runtime.getNativeTypeSize(type);
if (bytes > align) {
- var ret = '/* unaligned */';
- if (bytes <= 4) {
- ret += 'tempBigInt=' + value + ';';
- for (var i = 0; i < bytes; i++) {
- ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore) + ';';
- if (i < bytes-1) ret += 'tempBigInt>>=8;';
+ var ret = '';
+ if (isIntImplemented(type)) {
+ if (bytes <= 4) {
+ ret += 'tempBigInt=' + value + ';';
+ for (var i = 0; i < bytes; i++) {
+ ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore) + ';';
+ if (i < bytes-1) ret += 'tempBigInt>>=8;';
+ }
+ } else {
+ assert(bytes == 8);
+ ret += 'tempPair=' + ensureI64_1(value) + ';';
+ ret += makeSetValue(ptr, pos, 'tempPair[0]', 'i32', noNeedFirst, ignore, align) + ';';
+ ret += makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempPair[1]', 'i32', noNeedFirst, ignore, align) + ';';
}
} else {
- assert(bytes == 8);
- ret += 'tempPair=' + ensureI64_1(value) + ';';
- ret += makeSetValue(ptr, pos, 'tempPair[0]', 'i32', noNeedFirst, ignore, align) + ';';
- ret += makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempPair[1]', 'i32', noNeedFirst, ignore, align) + ';';
+ ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8) + ';';
+ ret += makeCopyValues(getFastValue(ptr, '+', pos), 'tempDoublePtr', Runtime.getNativeTypeSize(type), type, null, align);
}
return ret;
}
@@ -1465,7 +1470,7 @@ function makeSignOp(value, type, op, force, ignore) {
var bits, full;
if (type in Runtime.INT_TYPES) {
bits = parseInt(type.substr(1));
- full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(correctSpecificSign() && !PGO) + (
+ full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign() && !PGO)) + (
PGO ? ', "' + (ignore ? '' : Debugging.getIdentifier()) + '"' : ''
) + ')';
// Always sign/unsign constants at compile time, regardless of CHECK/CORRECT
@@ -1473,7 +1478,7 @@ function makeSignOp(value, type, op, force, ignore) {
return eval(full).toString();
}
}
- if (!correctSigns() && !CHECK_SIGNS && !force) return value;
+ if ((ignore || !correctSigns()) && !CHECK_SIGNS && !force) return value;
if (type in Runtime.INT_TYPES) {
// shortcuts
if (!CHECK_SIGNS || ignore) {
@@ -1589,17 +1594,43 @@ function processMathop(item) {
case 'xor': {
return '[' + ident1 + '[0] ^ ' + ident2 + '[0], ' + ident1 + '[1] ^ ' + ident2 + '[1]]';
}
- case 'shl': {
- return '[' + ident1 + '[0] << ' + ident2 + ', ' +
- '('+ident1 + '[1] << ' + ident2 + ') | ((' + ident1 + '[0]&((Math.pow(2, ' + ident2 + ')-1)<<(32-' + ident2 + '))) >>> (32-' + ident2 + '))]';
- }
- case 'ashr': {
- return '[('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&(Math.pow(2, ' + ident2 + ')-1))<<(32-' + ident2 + ')),' +
- ident1 + '[1] >>> ' + ident2 + ']';
- }
+ case 'shl':
+ case 'ashr':
case 'lshr': {
- return '[('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&(Math.pow(2, ' + ident2 + ')-1))<<(32-' + ident2 + ')),' +
- ident1 + '[1] >>> ' + ident2 + ']';
+ assert(isNumber(ident2));
+ bits = parseInt(ident2);
+ var ander = Math.pow(2, bits)-1;
+ if (bits < 32) {
+ switch (op) {
+ case 'shl':
+ return '[' + ident1 + '[0] << ' + ident2 + ', ' +
+ '('+ident1 + '[1] << ' + ident2 + ') | ((' + ident1 + '[0]&(' + ander + '<<' + (32 - bits) + ')) >>> (32-' + ident2 + '))]';
+ case 'ashr':
+ return '[((('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&' + ander + ')<<' + (32 - bits) + ')) >> 0) >>> 0,' +
+ '(' + ident1 + '[1] >> ' + ident2 + ') >>> 0]';
+ case 'lshr':
+ return '[(('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&' + ander + ')<<' + (32 - bits) + ')) >>> 0,' +
+ ident1 + '[1] >>> ' + ident2 + ']';
+ }
+ } else if (bits == 32) {
+ switch (op) {
+ case 'shl':
+ return '[0, ' + ident1 + '[0]]';
+ case 'ashr':
+ return '[' + ident1 + '[1], (' + ident1 + '[1]|0) < 0 ? ' + ander + ' : 0]';
+ case 'lshr':
+ return '[' + ident1 + '[1], 0]';
+ }
+ } else { // bits > 32
+ switch (op) {
+ case 'shl':
+ return '[0, ' + ident1 + '[0] << ' + (bits - 32) + ']';
+ case 'ashr':
+ return '[(' + ident1 + '[1] >> ' + (bits - 32) + ') >>> 0, (' + ident1 + '[1]|0) < 0 ? ' + ander + ' : 0]';
+ case 'lshr':
+ return '[' + ident1 + '[1] >>> ' + (bits - 32) + ', 0]';
+ }
+ }
}
case 'uitofp': case 'sitofp': return ident1 + '[0] + ' + ident1 + '[1]*4294967296';
case 'fptoui': case 'fptosi': return splitI64(ident1);
diff --git a/src/preamble.js b/src/preamble.js
index b9da766f..94add7f4 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -344,13 +344,6 @@ var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI
#if I64_MODE == 1
var tempI64, tempI64b;
#endif
-#if DOUBLE_MODE == 1
-#if USE_TYPED_ARRAYS == 2
-var tempDoubleBuffer = new ArrayBuffer(8);
-var tempDoubleI32 = new Int32Array(tempDoubleBuffer);
-var tempDoubleF64 = new Float64Array(tempDoubleBuffer);
-#endif
-#endif
function abort(text) {
print(text + ':\n' + (new Error).stack);
@@ -369,6 +362,7 @@ function assert(condition, text) {
// makeSetValue is done at compile-time and generates the needed
// code then, whereas this function picks the right code at
// run-time.
+// Note that setValue and getValue only do *aligned* writes and reads!
function setValue(ptr, value, type, noSafe) {
type = type || 'i8';
@@ -646,6 +640,33 @@ Module['HEAPF32'] = HEAPF32;
STACK_ROOT = STACKTOP = Runtime.alignMemory(STATICTOP);
STACK_MAX = STACK_ROOT + TOTAL_STACK;
+#if DOUBLE_MODE == 1
+#if USE_TYPED_ARRAYS == 2
+var tempDoublePtr = Runtime.alignMemory(STACK_MAX, 8);
+var tempDoubleI8 = HEAP8.subarray(tempDoublePtr);
+var tempDoubleI32 = HEAP32.subarray(tempDoublePtr >> 2);
+var tempDoubleF32 = HEAPF32.subarray(tempDoublePtr >> 2);
+var tempDoubleF64 = new Float64Array(HEAP8.buffer).subarray(tempDoublePtr >> 3);
+function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much
+ tempDoubleI8[0] = HEAP8[ptr];
+ tempDoubleI8[1] = HEAP8[ptr+1];
+ tempDoubleI8[2] = HEAP8[ptr+2];
+ tempDoubleI8[3] = HEAP8[ptr+3];
+}
+function copyTempDouble(ptr) {
+ tempDoubleI8[0] = HEAP8[ptr];
+ tempDoubleI8[1] = HEAP8[ptr+1];
+ tempDoubleI8[2] = HEAP8[ptr+2];
+ tempDoubleI8[3] = HEAP8[ptr+3];
+ tempDoubleI8[4] = HEAP8[ptr+4];
+ tempDoubleI8[5] = HEAP8[ptr+5];
+ tempDoubleI8[6] = HEAP8[ptr+6];
+ tempDoubleI8[7] = HEAP8[ptr+7];
+}
+STACK_MAX = tempDoublePtr + 8;
+#endif
+#endif
+
STATICTOP = alignMemoryPage(STACK_MAX);
function callRuntimeCallbacks(callbacks) {
diff --git a/src/runtime.js b/src/runtime.js
index 6439d0ed..6f17028a 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -73,6 +73,15 @@ var RuntimeGenerator = {
quantum = '(quantum ? quantum : {{{ QUANTUM_SIZE }}})';
}
return target + ' = ' + Runtime.forceAlign(target, quantum);
+ },
+
+ // Given two 32-bit unsigned parts of an emulated 64-bit number, combine them into a JS number (double).
+ // Rounding is inevitable if the number is large. This is a particular problem for small negative numbers
+ // (-1 will be rounded!), so handle negatives separately and carefully
+ makeBigInt: function(low, high, unsigned) {
+ return '(' + unsigned +
+ ' ? (' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 'un', 1, 1) + '*4294967296))' +
+ ' : (' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 're', 1, 1) + '*4294967296)))';
}
};
@@ -260,6 +269,7 @@ var Runtime = {
Runtime.stackAlloc = unInline('stackAlloc', ['size']);
Runtime.staticAlloc = unInline('staticAlloc', ['size']);
Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum']);
+Runtime.makeBigInt = unInline('makeBigInt', ['low', 'high', 'unsigned']);
function getRuntime() {
var ret = 'var Runtime = {\n';
diff --git a/src/settings.js b/src/settings.js
index 7e900ea9..ae07b1f4 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -73,13 +73,6 @@ var DOUBLE_MODE = 1; // How to load and store 64-bit doubles. Without typed arra
// then load it aligned, and that load-store will make JS engines alter it if it is being
// stored to a typed array for security reasons. That will 'fix' the number from being a
// NaN or an infinite number.
-var EMULATE_UNALIGNED_ACCESSES = 0; // If set, the compiler will 'emulate' loads and stores that are not known to
- // be sufficiently aligned, by working on individual bytes. This can be
- // important in USE_TYPED_ARRAYS == 2, where unaligned accesses do not work,
- // specifically in the case where unsafe LLVM optimizations have generated possibly
- // unaligned code. (Without unsafe LLVM optimizations, there should be no
- // need for this option.)
- // Currently this only works for integers, not doubles and floats.
var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
// compiler. This potentially lets closure optimize the code better.