diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 320 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 9 | ||||
-rw-r--r-- | src/library.js | 11 | ||||
-rw-r--r-- | src/parseTools.js | 169 | ||||
-rw-r--r-- | src/preamble.js | 35 | ||||
-rw-r--r-- | src/runtime.js | 10 | ||||
-rw-r--r-- | src/settings.js | 8 |
8 files changed, 478 insertions, 86 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 7412be6d..7eca1d5a 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -67,7 +67,7 @@ function analyzer(data, sidePass) { if (subItem.intertype == 'function') { item.functions.push(subItem); subItem.endLineNum = null; - subItem.lines = []; + subItem.lines = []; // We will fill in the function lines after the legalizer, since it can modify them subItem.labels = []; // no explicit 'entry' label in clang on LLVM 2.8 - most of the time, but not all the time! - so we add one if necessary @@ -87,7 +87,6 @@ function analyzer(data, sidePass) { } else if (item.functions.length > 0 && item.functions.slice(-1)[0].endLineNum === null) { // Internal line if (!currLabelFinished) { - item.functions.slice(-1)[0].lines.push(subItem); item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label? LLVM_STYLE related? if (subItem.intertype === 'branch') { currLabelFinished = true; @@ -100,7 +99,316 @@ function analyzer(data, sidePass) { } } delete item.items; - this.forwardItem(item, 'Typevestigator'); + this.forwardItem(item, 'Legalizer'); + } + }); + + // Legalize LLVM unrealistic types into realistic types. + // + // With full LLVM optimizations, it can generate types like i888 which do not exist in + // any actual hardware implementation, but are useful during optimization. LLVM then + // legalizes these types into real ones during code generation. Sadly, there is no LLVM + // IR pass to legalize them, which would have been useful and nice from a design perspective. + // The LLVM community is also not interested in receiving patches to implement that + // functionality, since it would duplicate existing code from the code generation + // component. Therefore, we implement legalization here in Emscripten. + // + // Currently we just legalize completely unrealistic types into bundles of i32s, and just + // the most common instructions that can be involved with such types: load, store, shifts, + // trunc and zext. + // + // TODO: Expand this also into legalization of i64 into i32,i32, which can then + // replace our i64 mode 1 implementation. Legalizing i64s is harder though + // as they can appear in function arguments and we would also need to implement + // an unfolder (to uninline inline LLVM function calls, so that each LLVM line + // has a single LLVM instruction). + substrate.addActor('Legalizer', { + processItem: function(data) { + // Legalization + if (USE_TYPED_ARRAYS == 2) { + function isIllegalType(type) { + return getBits(type) > 64; + } + function getLegalVars(base, bits) { + var ret = new Array(Math.ceil(bits/32)); + var i = 0; + while (bits > 0) { + ret[i] = { ident: base + '$' + i, bits: Math.min(32, bits) }; + bits -= 32; + i++; + } + return ret; + } + function getLegalLiterals(text, bits) { + var parsed = parseArbitraryInt(text, bits); + var ret = new Array(Math.ceil(bits/32)); + var i = 0; + while (bits > 0) { + ret[i] = { ident: parsed[i], bits: Math.min(32, bits) }; + bits -= 32; + i++; + } + return ret; + } + data.functions.forEach(function(func) { + func.labels.forEach(function(label) { + var i = 0, bits; + while (i < label.lines.length) { + var item = label.lines[i]; + if (item.intertype == 'store') { + if (isIllegalType(item.valueType)) { + dprint('legalizer', 'Legalizing store at line ' + item.lineNum); + bits = getBits(item.valueType); + assert(item.value.intertype == 'value', 'TODO: unfolding'); + var elements; + if (isNumber(item.value.ident)) { + elements = getLegalLiterals(item.value.ident, bits); + } else { + elements = getLegalVars(item.value.ident, bits); + } + label.lines.splice(i, 1); + var j = 0; + elements.forEach(function(element) { + var tempVar = '$st$' + i + '$' + j; + label.lines.splice(i+j*2, 0, { + intertype: 'assign', + ident: tempVar, + value: { + intertype: 'getelementptr', + ident: item.pointer.ident, + type: '[0 x i32]*', + params: [ + { intertype: 'value', ident: item.pointer.ident, type: '[0 x i32]*' }, // technically a bitcase is needed in llvm, but not for us + { intertype: 'value', ident: '0', type: 'i32' }, + { intertype: 'value', ident: j.toString(), type: 'i32' } + ], + }, + lineNum: item.lineNum + (j/100) + }); + var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits + label.lines.splice(i+j*2+1, 0, { + intertype: 'store', + valueType: actualSizeType, + value: { intertype: 'value', ident: element.ident, type: actualSizeType }, + pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, + ident: tempVar, + pointerType: actualSizeType + '*', + align: item.align, + lineNum: item.lineNum + ((j+0.5)/100) + }); + j++; + }); + Types.needAnalysis['[0 x i32]'] = 0; + i += j*2; + continue; + } + } else if (item.intertype == 'assign') { + var value = item.value; + switch (value.intertype) { + case 'load': { + if (isIllegalType(value.valueType)) { + dprint('legalizer', 'Legalizing load at line ' + item.lineNum); + bits = getBits(value.valueType); + assert(value.pointer.intertype == 'value', 'TODO: unfolding'); + var elements = getLegalVars(item.ident, bits); + label.lines.splice(i, 1); + var j = 0; + elements.forEach(function(element) { + var tempVar = '$st$' + i + '$' + j; + label.lines.splice(i+j*2, 0, { + intertype: 'assign', + ident: tempVar, + value: { + intertype: 'getelementptr', + ident: value.pointer.ident, + type: '[0 x i32]*', + params: [ + { intertype: 'value', ident: value.pointer.ident, type: '[0 x i32]*' }, // technically bitcast is needed in llvm, but not for us + { intertype: 'value', ident: '0', type: 'i32' }, + { intertype: 'value', ident: j.toString(), type: 'i32' } + ], + }, + lineNum: item.lineNum + (j/100) + }); + var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits + label.lines.splice(i+j*2+1, 0, { + intertype: 'assign', + ident: element.ident, + value: { + intertype: 'load', + pointerType: actualSizeType + '*', + valueType: actualSizeType, + type: actualSizeType, // XXX why is this missing from intertyper? + pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, + ident: tempVar, + pointerType: actualSizeType + '*', + align: value.align, + }, + lineNum: item.lineNum + ((j+0.5)/100) + }); + j++; + }); + Types.needAnalysis['[0 x i32]'] = 0; + i += j*2; + continue; + } + } + case 'mathop': { + if (isIllegalType(value.type)) { + dprint('legalizer', 'Legalizing mathop at line ' + item.lineNum); + label.lines.splice(i, 1); + var toAdd = []; + assert(value.param1.intertype == 'value', 'TODO: unfolding'); + var sourceBits = getBits(value.param1.type); + var sourceElements; + if (sourceBits <= 64) { + // The input is a legal type + if (sourceBits <= 32) { + sourceElements = [{ ident: value.param1.ident, bits: sourceBits }]; + } else if (sourceBits == 64 && I64_MODE == 1) { + sourceElements = [{ ident: value.param1.ident + '[0]', bits: 32 }, + { ident: value.param1.ident + '[1]', bits: 32 }]; + // Add the source element as a param so that it is not eliminated as unneeded (the idents are not a simple ident here) + toAdd.push({ + intertype: 'value', ident: ';', type: 'rawJS', + params: [{ intertype: 'value', ident: value.param1.ident, type: 'i32' }] + }); + } else { + throw 'Invalid legal type as source of legalization ' + sourceBits; + } + } else { + sourceElements = getLegalVars(value.param1.ident, sourceBits); + } + // All mathops can be parametrized by how many shifts we do, and how big the source is + var shifts = 0; + var targetBits; + switch (value.op) { + case 'lshr': { + assert(value.param2.intertype == 'value', 'TODO: unfolding'); + shifts = parseInt(value.param2.ident); + targetBits = sourceBits; + break; + } + case 'shl': { + assert(value.param2.intertype == 'value', 'TODO: unfolding'); + shifts = -parseInt(value.param2.ident); + targetBits = sourceBits; + break; + } + case 'trunc': case 'zext': { + assert(value.param2.intertype == 'type' || value.param2.intertype == 'value', 'TODO: unfolding'); + targetBits = getBits(value.param2.ident); + break; + } + default: throw 'Invalid mathop for legalization: ' + [value.op, item.lineNum, dump(item)]; + } + // Do the legalization + assert(isNumber(shifts), 'TODO: handle nonconstant shifts'); + var targetElements = getLegalVars(item.ident, targetBits); + var sign = shifts >= 0 ? 1 : -1; + var shiftOp = shifts >= 0 ? 'shl' : 'lshr'; + var shiftOpReverse = shifts >= 0 ? 'lshr' : 'shl'; + var whole = shifts >= 0 ? Math.floor(shifts/32) : Math.ceil(shifts/32); + var fraction = Math.abs(shifts % 32); + for (var j = 0; j < targetElements.length; j++) { + var result = { + intertype: 'value', + ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : '0', + type: 'i32', + }; + if (fraction != 0) { + var other = { + intertype: 'value', + ident: (j + sign + whole >= 0 && j + sign + whole < sourceElements.length) ? sourceElements[j + sign + whole].ident : '0', + type: 'i32', + }; + other = { + intertype: 'mathop', + op: shiftOp, + type: 'i32', + param1: other, + param2: { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' } + }; + result = { + intertype: 'mathop', + op: shiftOpReverse, + type: 'i32', + param1: result, + param2: { intertype: 'value', ident: fraction.toString(), type: 'i32' } + }; + result = { + intertype: 'mathop', + op: 'or', + type: 'i32', + param1: result, + param2: other + } + } + if (targetElements[j].bits < 32 && shifts < 0) { + // truncate bits that fall off the end. This is not needed in most cases, can probably be optimized out + result = { + intertype: 'mathop', + op: 'and', + type: 'i32', + param1: result, + param2: { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' } + } + } + toAdd.push({ + intertype: 'assign', + ident: targetElements[j].ident, + value: result, + lineNum: item.lineNum + (j/100) + }); + } + if (targetBits <= 64) { + // We are generating a normal legal type here + var legalValue; + if (targetBits == 64 && I64_MODE == 1) { + // Generate an i64-1 [low,high]. This will be unnecessary when we legalize i64s + legalValue = { + intertype: 'value', + ident: '[' + targetElements[0].ident + ',' + targetElements[1].ident + ']', + type: 'rawJS', + // Add the target elements as params so that they are not eliminated as unneeded (the ident is not a simple ident here) + params: targetElements.map(function(element) { + return { intertype: 'value', ident: element.ident, type: 'i32' }; + }) + }; + } else if (targetBits <= 32) { + legalValue = { intertype: 'value', ident: targetElements[0].ident, type: 'rawJS' }; + // truncation to smaller than 32 bits has already been done, if necessary + } else { + throw 'Invalid legal type as target of legalization ' + targetBits; + } + toAdd.push({ + intertype: 'assign', + ident: item.ident, + value: legalValue, + lineNum: item.lineNum + ((j+1)/100) + }); + } + Array.prototype.splice.apply(label.lines, [i, 0].concat(toAdd)); + i += toAdd.length; + continue; + } + } + } + } + i++; + continue; + } + }); + }); + } + + // Add function lines to func.lines, after our modifications to the label lines + data.functions.forEach(function(func) { + func.labels.forEach(function(label) { + func.lines = func.lines.concat(label.lines); + }); + }); + this.forwardItem(data, 'Typevestigator'); } }); @@ -463,6 +771,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/intertyper.js b/src/intertyper.js index ae9794b8..cf1d28ed 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -864,7 +864,7 @@ function intertyper(data, sidePass, baseLineNums) { var ret = { intertype: 'store', valueType: item.tokens[1].text, - value: parseLLVMSegment(segments[0]), // TODO: Make everything use this method, with finalizeLLVMParameter too + value: parseLLVMSegment(segments[0]), pointer: parseLLVMSegment(segments[1]), lineNum: item.lineNum }; diff --git a/src/jsifier.js b/src/jsifier.js index 657a9673..62cab3d5 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({ @@ -719,6 +719,9 @@ function JSify(data, functionsOnly, givenFunctions) { } }); } + makeFuncLineActor('value', function(item) { + return item.ident; + }); makeFuncLineActor('noop', function(item) { return ';'; }); @@ -824,7 +827,7 @@ function JSify(data, functionsOnly, givenFunctions) { }); labelSets.forEach(function(labelSet) { walkInterdata(labelSet.value, function mark(item) { - if (item.intertype == 'value' && item.ident in deps) { + if (item.intertype == 'value' && item.ident in deps && labelSet.ident != item.ident) { deps[labelSet.ident][item.ident] = true; } }); @@ -845,7 +848,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } // If we got here, we have circular dependencies, and must break at least one. - pre = 'var ' + idents[0] + '$phi = ' + valueJSes[idents[i]] + ';' + pre; + pre = 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';' + pre; post += 'var ' + idents[0] + ' = ' + idents[0] + '$phi;'; remove(idents[0]); } diff --git a/src/library.js b/src/library.js index 45c64bc7..47e15aae 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..d4ef27eb 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -129,6 +129,11 @@ function isIntImplemented(type) { return type[0] == 'i' || isPointerType(type); } +function getBits(type) { + if (!type || type[0] != 'i') return 0; + return parseInt(type.substr(1)); +} + function isVoidType(type) { return type == 'void'; } @@ -558,22 +563,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 +585,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 @@ -604,13 +600,12 @@ function makeCopyI64(value) { return value + '.slice(0)'; } -function parseI64Constant(str) { - assert(I64_MODE == 1); +// 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. - if (!isNumber(str)) { - // This is a variable. Copy it, so we do not modify the original - return makeCopyI64(str); - } + assert(bits % 32 == 0 || ('i' + (bits % 32)) in Runtime.INT_TYPES, 'Arbitrary-sized ints must tails that are of legal size'); function str2vec(s) { // index 0 is the highest value var ret = []; @@ -668,6 +663,7 @@ function parseI64Constant(str) { if (str[0] == '-') { // twos-complement is needed + assert(bits == 64, "we only support 64-bit two's complement so far"); str = str.substr(1); v = str2vec('18446744073709551616'); // 2^64 subtract(v, str2vec(str)); @@ -675,23 +671,30 @@ function parseI64Constant(str) { v = str2vec(str); } - var bits = []; + var bitsv = []; while (!isZero(v)) { - bits.push((v[v.length-1] % 2 != 0)+0); + bitsv.push((v[v.length-1] % 2 != 0)+0); v[v.length-1] = v[v.length-1] & 0xfe; divide2(v); } - var low = 0, high = 0; - for (var i = 0; i < bits.length; i++) { - if (i <= 31) { - low += bits[i]*Math.pow(2, i); - } else { - high += bits[i]*Math.pow(2, i-32); - } + 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; +} - return '[' + low + ',' + high + ']'; +function parseI64Constant(str) { + assert(I64_MODE == 1); + + if (!isNumber(str)) { + // This is a variable. Copy it, so we do not modify the original + return makeCopyI64(str); + } + + var parsed = parseArbitraryInt(str, 64); + return '[' + parsed[0] + ',' + parsed[1] + ']'; } function parseNumerical(value, type) { @@ -912,23 +915,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 +1006,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; } @@ -1447,6 +1464,8 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) { return finalizeBlockAddress(param); } else if (param.intertype === 'type') { return param.ident; // we don't really want the type here + } else if (param.intertype == 'mathop') { + return processMathop(param); } else { throw 'invalid llvm parameter: ' + param.intertype; } @@ -1465,7 +1484,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 +1492,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 +1608,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 9370fa54..d0391af5 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'; @@ -648,6 +642,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, |