diff options
Diffstat (limited to 'src/analyzer.js')
-rw-r--r-- | src/analyzer.js | 212 |
1 files changed, 147 insertions, 65 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 14a4f7de..8ded86f1 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -120,10 +120,6 @@ function analyzer(data, sidePass) { processItem: function(data) { // Legalization if (USE_TYPED_ARRAYS == 2) { - function isIllegalType(type) { - var bits = getBits(type); - return bits > 0 && (bits >= 64 || !isPowerOfTwo(bits)); - } function getLegalVars(base, bits) { assert(!isNumber(base)); var ret = new Array(Math.ceil(bits/32)); @@ -140,7 +136,7 @@ function analyzer(data, sidePass) { var ret = new Array(Math.ceil(bits/32)); var i = 0; while (bits > 0) { - ret[i] = { ident: parsed[i].toString(), bits: Math.min(32, bits) }; + ret[i] = { ident: (parsed[i]|0).toString(), bits: Math.min(32, bits) }; // resign all values bits -= 32; i++; } @@ -182,6 +178,13 @@ function analyzer(data, sidePass) { i++; } } + function fixUnfolded(item) { + // Unfolded items may need some correction to work properly in the global scope + if (item.intertype in MATHOPS) { + item.op = item.intertype; + item.intertype = 'mathop'; + } + } data.functions.forEach(function(func) { // Legalize function params legalizeFunctionParameters(func.params); @@ -218,10 +221,29 @@ function analyzer(data, sidePass) { // accessible through ident$x, and not constants we need to parse then and there) if (subItem != item && (!(subItem.intertype in UNUNFOLDABLE) || (subItem.intertype == 'value' && isNumber(subItem.ident) && isIllegalType(subItem.type)))) { - var tempIdent = '$$emscripten$temp$' + (tempId++); - subItem.assignTo = tempIdent; - unfolded.unshift(subItem); - return { intertype: 'value', ident: tempIdent, type: subItem.type }; + if (item.intertype == 'phi') { + assert(subItem.intertype == 'value', 'We can only unfold illegal constants in phis'); + // we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi + } else { + var tempIdent = '$$emscripten$temp$' + (tempId++); + subItem.assignTo = tempIdent; + unfolded.unshift(subItem); + fixUnfolded(subItem); + return { intertype: 'value', ident: tempIdent, type: subItem.type }; + } + } else if (subItem.intertype == 'switch' && isIllegalType(subItem.type)) { + subItem.switchLabels.forEach(function(switchLabel) { + if (switchLabel.value[0] != '$') { + var tempIdent = '$$emscripten$temp$' + (tempId++); + unfolded.unshift({ + assignTo: tempIdent, + intertype: 'value', + ident: switchLabel.value, + type: subItem.type + }); + switchLabel.value = tempIdent; + } + }); } }); if (unfolded.length > 0) { @@ -268,7 +290,7 @@ function analyzer(data, sidePass) { continue; } // call, return: Return value is in an unlegalized array literal. Not fully optimal. - case 'call': case 'invoke': { + case 'call': { bits = getBits(value.type); var elements = getLegalVars(item.assignTo, bits); var toAdd = [value]; @@ -296,6 +318,12 @@ function analyzer(data, sidePass) { i++; continue; } + case 'invoke': { + legalizeFunctionParameters(value.params); + // We can't add lines after this, since invoke already modifies control flow. So we handle the return in invoke + i++; + continue; + } case 'value': { bits = getBits(value.type); var elements = getLegalVars(item.assignTo, bits); @@ -353,6 +381,12 @@ function analyzer(data, sidePass) { var toAdd = []; var elements = getLegalVars(item.assignTo, bits); var j = 0; + var literalValues = {}; // special handling of literals - we cannot unfold them normally + value.params.map(function(param) { + if (isNumber(param.value.ident)) { + literalValues[param.value.ident] = getLegalLiterals(param.value.ident, bits); + } + }); elements.forEach(function(element) { toAdd.push({ intertype: 'phi', @@ -364,7 +398,7 @@ function analyzer(data, sidePass) { label: param.label, value: { intertype: 'value', - ident: param.value.ident + '$' + j, + ident: (param.value.ident in literalValues) ? literalValues[param.value.ident][j].ident : (param.value.ident + '$' + j), type: 'i' + element.bits, } }; @@ -375,6 +409,10 @@ function analyzer(data, sidePass) { i += removeAndAdd(label.lines, i, toAdd); continue; } + case 'switch': { + i++; + continue; // special case, handled in makeComparison + } case 'bitcast': { var inType = item.type2; var outType = item.type; @@ -385,16 +423,18 @@ function analyzer(data, sidePass) { } // fall through } - case 'inttoptr': case 'ptrtoint': { + case 'inttoptr': case 'ptrtoint': case 'zext': case 'sext': case 'trunc': case 'ashr': case 'lshr': case 'shl': case 'or': case 'and': case 'xor': { value = { op: item.intertype, - param1: item.params[0] + variant: item.variant, + type: item.type, + params: item.params }; // fall through } case 'mathop': { var toAdd = []; - var sourceBits = getBits(value.param1.type); + var sourceBits = getBits(value.params[0].type); // All mathops can be parametrized by how many shifts we do, and how big the source is var shifts = 0; var targetBits = sourceBits; @@ -406,11 +446,11 @@ function analyzer(data, sidePass) { // fall through } case 'lshr': { - shifts = parseInt(value.param2.ident); + shifts = parseInt(value.params[1].ident); break; } case 'shl': { - shifts = -parseInt(value.param2.ident); + shifts = -parseInt(value.params[1].ident); break; } case 'sext': { @@ -418,7 +458,7 @@ function analyzer(data, sidePass) { // fall through } case 'trunc': case 'zext': case 'ptrtoint': { - targetBits = getBits(value.param2.ident); + targetBits = getBits(value.params[1] ? value.params[1].ident : value.type); break; } case 'inttoptr': { @@ -429,31 +469,35 @@ function analyzer(data, sidePass) { break; } case 'select': { - sourceBits = targetBits = getBits(value.param2.type); - var otherElementsA = getLegalVars(value.param2.ident, sourceBits); - var otherElementsB = getLegalVars(value.param3.ident, sourceBits); + sourceBits = targetBits = getBits(value.params[1].type); + var otherElementsA = getLegalVars(value.params[1].ident, sourceBits); + var otherElementsB = getLegalVars(value.params[2].ident, sourceBits); processor = function(result, j) { return { intertype: 'mathop', op: 'select', type: 'i' + otherElementsA[j].bits, - param1: value.param1, - param2: { intertype: 'value', ident: otherElementsA[j].ident, type: 'i' + otherElementsA[j].bits }, - param3: { intertype: 'value', ident: otherElementsB[j].ident, type: 'i' + otherElementsB[j].bits } + params: [ + value.params[0], + { intertype: 'value', ident: otherElementsA[j].ident, type: 'i' + otherElementsA[j].bits }, + { intertype: 'value', ident: otherElementsB[j].ident, type: 'i' + otherElementsB[j].bits } + ] }; }; break; } case 'or': case 'and': case 'xor': { - var otherElements = getLegalVars(value.param2.ident, sourceBits); + var otherElements = getLegalVars(value.params[1].ident, sourceBits); processor = function(result, j) { return { intertype: 'mathop', op: value.op, type: 'i' + otherElements[j].bits, - param1: result, - param2: { intertype: 'value', ident: otherElements[j].ident, type: 'i' + otherElements[j].bits } - }; + params: [ + result, + { intertype: 'value', ident: otherElements[j].ident, type: 'i' + otherElements[j].bits } + ] + }; }; break; } @@ -469,16 +513,16 @@ function analyzer(data, sidePass) { var sourceElements; if (sourceBits <= 32) { // The input is a legal type - sourceElements = [{ ident: value.param1.ident, bits: sourceBits }]; + sourceElements = [{ ident: value.params[0].ident, bits: sourceBits }]; } else { - sourceElements = getLegalVars(value.param1.ident, sourceBits); + sourceElements = getLegalVars(value.params[0].ident, sourceBits); } if (!isNumber(shifts)) { // We can't statically legalize this, do the operation at runtime TODO: optimize assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits'); value.intertype = 'value'; value.ident = 'Runtime.bitshift64(' + sourceElements[0].ident + ', ' + - sourceElements[1].ident + ',"' + value.op + '",' + value.param2.ident + '$0);' + + sourceElements[1].ident + ',"' + value.op + '",' + value.params[1].ident + '$0);' + 'var ' + value.assignTo + '$0 = ' + value.assignTo + '[0], ' + value.assignTo + '$1 = ' + value.assignTo + '[1];'; i++; continue; @@ -497,37 +541,43 @@ function analyzer(data, sidePass) { var result = { intertype: 'value', ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : (signed ? signedFill : '0'), - param1: (signed && j + whole > sourceElements.length) ? signedKeepAlive : null, + params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null], type: 'i32', }; if (fraction != 0) { var other = { intertype: 'value', ident: (j + sign + whole >= 0 && j + sign + whole < sourceElements.length) ? sourceElements[j + sign + whole].ident : (signed ? signedFill : '0'), - param1: (signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null, + params: [(signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null], type: 'i32', }; other = { intertype: 'mathop', op: shiftOp, type: 'i32', - param1: other, - param2: { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' } + params: [ + other, + { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' } + ] }; result = { intertype: 'mathop', // shifting in 1s from the top is a special case op: (signed && shifts >= 0 && j + sign + whole >= sourceElements.length) ? 'ashr' : shiftOpReverse, type: 'i32', - param1: result, - param2: { intertype: 'value', ident: fraction.toString(), type: 'i32' } + params: [ + result, + { intertype: 'value', ident: fraction.toString(), type: 'i32' } + ] }; result = { intertype: 'mathop', op: 'or', type: 'i32', - param1: result, - param2: other + params: [ + result, + other + ] } } if (targetElements[j].bits < 32 && shifts < 0) { @@ -536,8 +586,10 @@ function analyzer(data, sidePass) { intertype: 'mathop', op: 'and', type: 'i32', - param1: result, - param2: { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' } + params: [ + result, + { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' } + ] } } if (processor) { @@ -548,8 +600,11 @@ function analyzer(data, sidePass) { } if (targetBits <= 32) { // We are generating a normal legal type here - legalValue = { intertype: 'value', ident: targetElements[0].ident, type: 'rawJS' }; - // truncation to smaller than 32 bits has already been done, if necessary + legalValue = { + intertype: 'value', + ident: targetElements[0].ident + (targetBits < 32 ? '&' + (Math.pow(2, targetBits)-1) : ''), + type: 'rawJS' + }; legalValue.assignTo = item.assignTo; toAdd.push(legalValue); } @@ -855,7 +910,7 @@ function analyzer(data, sidePass) { variable.impl = VAR_EMULATED; } else if (variable.origin == 'funcparam') { variable.impl = VAR_EMULATED; - } else if (variable.type == 'i64*' && I64_MODE == 1) { + } else if (variable.type == 'i64*' && USE_TYPED_ARRAYS == 2) { variable.impl = VAR_EMULATED; } else if (MICRO_OPTS && variable.pointingLevels === 0) { // A simple int value, can be implemented as a native variable @@ -1096,18 +1151,61 @@ function analyzer(data, sidePass) { } }); + function operateOnLabels(line, func) { + function process(item, id) { + ['label', 'labelTrue', 'labelFalse', 'toLabel', 'unwindLabel', 'defaultLabel'].forEach(function(id) { + if (item[id]) { + func(item, id); + } + }); + } + if (line.intertype in BRANCH_INVOKE) { + process(line); + } else if (line.intertype == 'switch') { + process(line); + line.switchLabels.forEach(process); + } + } + // Label analyzer substrate.addActor('LabelAnalyzer', { processItem: function(item) { item.functions.forEach(function(func) { func.labelsDict = {}; func.labelIds = {}; - func.labelIdCounter = 0; + func.labelIdsInverse = {}; + func.labelIds[toNiceIdent('%0')] = 0; + func.labelIdsInverse[0] = toNiceIdent('%0'); + func.labelIdCounter = 1; func.labels.forEach(function(label) { - func.labelsDict[label.ident] = label; func.labelIds[label.ident] = func.labelIdCounter++; + func.labelIdsInverse[func.labelIdCounter-1] = label.ident; + }); + + // Minify label ids to numeric ids. + func.labels.forEach(function(label) { + label.ident = func.labelIds[label.ident]; + label.lines.forEach(function(line) { + operateOnLabels(line, function(item, id) { + item[id] = func.labelIds[item[id]].toString(); // strings, because we will append as we process + }); + }); + }); + + func.labels.forEach(function(label) { + func.labelsDict[label.ident] = label; + }); + + // Correct phis + func.labels.forEach(function(label) { + label.lines.forEach(function(phi) { + if (phi.intertype == 'phi') { + for (var i = 0; i < phi.params.length; i++) { + phi.params[i].label = func.labelIds[phi.params[i].label]; + } + } + }); }); - func.labelIds[toNiceIdent('%0')] = -1; // entry is always -1 func.lines.forEach(function(line) { if (line.intertype == 'indirectbr') { @@ -1136,7 +1234,7 @@ function analyzer(data, sidePass) { if (line.intertype == 'call' && line.ident == setjmp) { // Add a new label var oldIdent = label.ident; - var newIdent = oldIdent + '$$' + i; + var newIdent = func.labelIdCounter++; if (!func.setjmpTable) func.setjmpTable = []; func.setjmpTable.push([oldIdent, newIdent, line.assignTo]); func.labels.splice(i+1, 0, { @@ -1330,22 +1428,6 @@ function analyzer(data, sidePass) { } }); - function operateOnLabels(line, func) { - function process(item, id) { - ['label', 'labelTrue', 'labelFalse', 'toLabel', 'unwindLabel', 'defaultLabel'].forEach(function(id) { - if (item[id]) { - func(item, id); - } - }); - } - if (line.intertype in BRANCH_INVOKE) { - process(line); - } else if (line.intertype == 'switch') { - process(line); - line.switchLabels.forEach(process); - } - } - //! @param toLabelId If false, just a dry run - useful to search for labels function replaceLabels(line, labelIds, toLabelId) { var ret = []; @@ -1436,7 +1518,7 @@ function analyzer(data, sidePass) { label.allOutLabels = []; }); - // First, find allInLabels + // First, find allInLabels. TODO: use typed arrays here to optimize this for memory and speed var more = true, nextModified, modified = set(getLabelIds(labels)); while (more) { more = false; @@ -1487,7 +1569,7 @@ function analyzer(data, sidePass) { var idCounter = 0; function makeBlockId(entries) { idCounter++; - return entries.join('$') + '$' + idCounter; + return '$_$' + idCounter; } // There are X main kinds of blocks: |