summaryrefslogtreecommitdiff
path: root/src/analyzer.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/analyzer.js')
-rw-r--r--src/analyzer.js2766
1 files changed, 1366 insertions, 1400 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index b20dedff..3fb20253 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -27,82 +27,72 @@ var SHADOW_FLIP = { i64: 'double', double: 'i64' }; //, i32: 'float', float: 'i3
function analyzer(data, sidePass) {
var mainPass = !sidePass;
- // Substrate
- var substrate = new Substrate('Analyzer');
-
- // Sorter
- substrate.addActor('Sorter', {
- processItem: function(item) {
- item.items.sort(function (a, b) { return a.lineNum - b.lineNum });
- this.forwardItem(item, 'Gatherer');
+ var item = { items: data };
+ var data = item;
+
+ var newTypes = {};
+
+ // Gather
+ // Single-liners
+ ['globalVariable', 'functionStub', 'unparsedFunction', 'unparsedGlobals', 'unparsedTypes', 'alias'].forEach(function(intertype) {
+ var temp = splitter(item.items, function(item) { return item.intertype == intertype });
+ item.items = temp.leftIn;
+ item[intertype + 's'] = temp.splitOut;
+ });
+ var temp = splitter(item.items, function(item) { return item.intertype == 'type' });
+ item.items = temp.leftIn;
+ temp.splitOut.forEach(function(type) {
+ //dprint('types', 'adding defined type: ' + type.name_);
+ Types.types[type.name_] = type;
+ newTypes[type.name_] = 1;
+ if (QUANTUM_SIZE === 1) {
+ Types.fatTypes[type.name_] = copy(type);
}
});
- // Gatherer
- substrate.addActor('Gatherer', {
- processItem: function(item) {
- // Single-liners
- ['globalVariable', 'functionStub', 'unparsedFunction', 'unparsedGlobals', 'unparsedTypes', 'alias'].forEach(function(intertype) {
- var temp = splitter(item.items, function(item) { return item.intertype == intertype });
- item.items = temp.leftIn;
- item[intertype + 's'] = temp.splitOut;
- });
- var temp = splitter(item.items, function(item) { return item.intertype == 'type' });
- item.items = temp.leftIn;
- temp.splitOut.forEach(function(type) {
- //dprint('types', 'adding defined type: ' + type.name_);
- Types.types[type.name_] = type;
- if (QUANTUM_SIZE === 1) {
- Types.fatTypes[type.name_] = copy(type);
- }
- });
-
- // Functions & labels
- item.functions = [];
- var currLabelFinished; // Sometimes LLVM puts a branch in the middle of a label. We need to ignore all lines after that.
- item.items.sort(function(a, b) { return a.lineNum - b.lineNum });
- for (var i = 0; i < item.items.length; i++) {
- var subItem = item.items[i];
- assert(subItem.lineNum);
- if (subItem.intertype == 'function') {
- item.functions.push(subItem);
- subItem.endLineNum = null;
- subItem.lines = []; // We will fill in the function lines after the legalizer, since it can modify them
- subItem.labels = [];
- subItem.forceEmulated = false;
-
- // 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
- if (item.items[i+1].intertype !== 'label') {
- item.items.splice(i+1, 0, {
- intertype: 'label',
- ident: ENTRY_IDENT,
- lineNum: subItem.lineNum + '.5'
- });
- }
- } else if (subItem.intertype == 'functionEnd') {
- item.functions.slice(-1)[0].endLineNum = subItem.lineNum;
- } else if (subItem.intertype == 'label') {
- item.functions.slice(-1)[0].labels.push(subItem);
- subItem.lines = [];
- currLabelFinished = false;
- } else if (item.functions.length > 0 && item.functions.slice(-1)[0].endLineNum === null) {
- // Internal line
- if (!currLabelFinished) {
- item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label?
- if (subItem.intertype in LABEL_ENDERS) {
- currLabelFinished = true;
- }
- } else {
- print('// WARNING: content after a branch in a label, line: ' + subItem.lineNum);
- }
- } else {
- throw 'ERROR: what is this? ' + dump(subItem);
+ // Functions & labels
+ item.functions = [];
+ var currLabelFinished = false; // Sometimes LLVM puts a branch in the middle of a label. We need to ignore all lines after that.
+ item.items.sort(function(a, b) { return a.lineNum - b.lineNum });
+ for (var i = 0; i < item.items.length; i++) {
+ var subItem = item.items[i];
+ assert(subItem.lineNum);
+ if (subItem.intertype == 'function') {
+ item.functions.push(subItem);
+ subItem.endLineNum = null;
+ subItem.lines = []; // We will fill in the function lines after the legalizer, since it can modify them
+ subItem.labels = [];
+ subItem.forceEmulated = false;
+
+ // 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
+ if (item.items[i+1].intertype !== 'label') {
+ item.items.splice(i+1, 0, {
+ intertype: 'label',
+ ident: ENTRY_IDENT,
+ lineNum: subItem.lineNum + '.5'
+ });
+ }
+ } else if (subItem.intertype == 'functionEnd') {
+ item.functions.slice(-1)[0].endLineNum = subItem.lineNum;
+ } else if (subItem.intertype == 'label') {
+ item.functions.slice(-1)[0].labels.push(subItem);
+ subItem.lines = [];
+ currLabelFinished = false;
+ } else if (item.functions.length > 0 && item.functions.slice(-1)[0].endLineNum === null) {
+ // Internal line
+ if (!currLabelFinished) {
+ item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label?
+ if (subItem.intertype in LABEL_ENDERS) {
+ currLabelFinished = true;
}
+ } else {
+ print('// WARNING: content after a branch in a label, line: ' + subItem.lineNum);
}
- delete item.items;
- this.forwardItem(item, 'CastAway');
+ } else {
+ throw 'ERROR: what is this? ' + dump(subItem);
}
- });
+ }
+ delete item.items;
// CastAway - try to remove bitcasts of double<-->i64, which LLVM sometimes generates unnecessarily
// (load a double, convert to i64, use as i64).
@@ -113,75 +103,72 @@ function analyzer(data, sidePass) {
// Note that aside from being an optimization, this is needed for correctness in some cases: If code
// assumes it can bitcast a double to an i64 and back and forth without loss, that may be violated
// due to NaN canonicalization.
- substrate.addActor('CastAway', {
- processItem: function(item) {
- this.forwardItem(item, 'Legalizer');
- if (USE_TYPED_ARRAYS != 2) return;
+ function castAway() {
+ if (USE_TYPED_ARRAYS != 2) return;
- item.functions.forEach(function(func) {
- var has = false;
- func.labels.forEach(function(label) {
- var lines = label.lines;
- for (var i = 0; i < lines.length; i++) {
- var line = lines[i];
- if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP) {
- has = true;
- }
+ item.functions.forEach(function(func) {
+ var has = false;
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP) {
+ has = true;
}
- });
- if (!has) return;
- // there are integer<->floating-point bitcasts, create shadows for everything
- var shadowed = {};
- func.labels.forEach(function(label) {
- var lines = label.lines;
- var i = 0;
- while (i < lines.length) {
- var lines = label.lines;
- var line = lines[i];
- if (line.intertype == 'load' && line.type in SHADOW_FLIP) {
- if (line.pointer.intertype != 'value') { i++; continue } // TODO
- shadowed[line.assignTo] = 1;
- var shadow = line.assignTo + '$$SHADOW';
- var flip = SHADOW_FLIP[line.type];
- lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase
- tokens: null,
- indent: 2,
- lineNum: line.lineNum + 0.5,
- assignTo: shadow,
- intertype: 'load',
- pointerType: flip + '*',
- type: flip,
- valueType: flip,
- pointer: {
- intertype: 'value',
- ident: line.pointer.ident,
- type: flip + '*'
- },
- align: line.align,
- ident: line.ident
- });
- // note: no need to update func.lines, it is generated in a later pass
- i++;
- }
+ }
+ });
+ if (!has) return;
+ // there are integer<->floating-point bitcasts, create shadows for everything
+ var shadowed = {};
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ var i = 0;
+ while (i < lines.length) {
+ var lines = label.lines;
+ var line = lines[i];
+ if (line.intertype == 'load' && line.type in SHADOW_FLIP) {
+ if (line.pointer.intertype != 'value') { i++; continue } // TODO
+ shadowed[line.assignTo] = 1;
+ var shadow = line.assignTo + '$$SHADOW';
+ var flip = SHADOW_FLIP[line.type];
+ lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase
+ tokens: null,
+ indent: 2,
+ lineNum: line.lineNum + 0.5,
+ assignTo: shadow,
+ intertype: 'load',
+ pointerType: flip + '*',
+ type: flip,
+ valueType: flip,
+ pointer: {
+ intertype: 'value',
+ ident: line.pointer.ident,
+ type: flip + '*'
+ },
+ align: line.align,
+ ident: line.ident
+ });
+ // note: no need to update func.lines, it is generated in a later pass
i++;
}
- });
- // use shadows where possible
- func.labels.forEach(function(label) {
- var lines = label.lines;
- for (var i = 0; i < lines.length; i++) {
- var line = lines[i];
- if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP && line.ident in shadowed) {
- var shadow = line.ident + '$$SHADOW';
- line.params[0].ident = shadow;
- line.params[0].type = line.type;
- line.type2 = line.type;
- }
+ i++;
+ }
+ });
+ // use shadows where possible
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP && line.ident in shadowed) {
+ var shadow = line.ident + '$$SHADOW';
+ line.params[0].ident = shadow;
+ line.params[0].type = line.type;
+ line.type2 = line.type;
}
- });
+ }
});
- }
- });
+ });
+ }
// Legalize LLVM unrealistic types into realistic types.
//
@@ -196,727 +183,724 @@ function analyzer(data, sidePass) {
// 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.
- substrate.addActor('Legalizer', {
- processItem: function(data) {
- // Legalization
- if (USE_TYPED_ARRAYS == 2) {
- function getLegalVars(base, bits, allowLegal) {
- bits = bits || 32; // things like pointers are all i32, but show up as 0 bits from getBits
- if (allowLegal && bits <= 32) return [{ ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits }];
- if (isNumber(base)) return getLegalLiterals(base, bits);
- if (base[0] == '{') {
- warnOnce('seeing source of illegal data ' + base + ', likely an inline struct - assuming zeroinit');
- return getLegalLiterals('0', bits);
- }
- var ret = new Array(Math.ceil(bits/32));
- var i = 0;
- if (base == 'zeroinitializer' || base == 'undef') base = 0;
- while (bits > 0) {
- ret[i] = { ident: base ? base + '$' + i : '0', 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]|0).toString(), bits: Math.min(32, bits) }; // resign all values
- bits -= 32;
- i++;
- }
- return ret;
+ function legalizer() {
+ // Legalization
+ if (USE_TYPED_ARRAYS == 2) {
+ function getLegalVars(base, bits, allowLegal) {
+ bits = bits || 32; // things like pointers are all i32, but show up as 0 bits from getBits
+ if (allowLegal && bits <= 32) return [{ ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits }];
+ if (isNumber(base)) return getLegalLiterals(base, bits);
+ if (base[0] == '{') {
+ warnOnce('seeing source of illegal data ' + base + ', likely an inline struct - assuming zeroinit');
+ return getLegalLiterals('0', bits);
}
- function getLegalStructuralParts(value) {
- return value.params.slice(0);
+ var ret = new Array(Math.ceil(bits/32));
+ var i = 0;
+ if (base == 'zeroinitializer' || base == 'undef') base = 0;
+ while (bits > 0) {
+ ret[i] = { ident: base ? base + '$' + i : '0', bits: Math.min(32, bits) };
+ bits -= 32;
+ i++;
}
- function getLegalParams(params, bits) {
- return params.map(function(param) {
- var value = param.value || param;
- if (isNumber(value.ident)) {
- return getLegalLiterals(value.ident, bits);
- } else if (value.intertype == 'structvalue') {
- return getLegalStructuralParts(value).map(function(part) {
- return { ident: part.ident, bits: part.type.substr(1) };
- });
- } else {
- return getLegalVars(value.ident, bits);
- }
- });
+ 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]|0).toString(), bits: Math.min(32, bits) }; // resign all values
+ bits -= 32;
+ i++;
}
- // Uses the right factor to multiply line numbers by so that they fit in between
- // the line[i] and the line after it
- function interpLines(lines, i, toAdd) {
- var prev = i >= 0 ? lines[i].lineNum : -1;
- var next = (i < lines.length-1) ? lines[i+1].lineNum : (lines[i].lineNum + 0.5);
- var factor = (next - prev)/(4*toAdd.length+3);
- for (var k = 0; k < toAdd.length; k++) {
- toAdd[k].lineNum = prev + ((k+1)*factor);
- assert(k == 0 || toAdd[k].lineNum > toAdd[k-1].lineNum);
+ return ret;
+ }
+ function getLegalStructuralParts(value) {
+ return value.params.slice(0);
+ }
+ function getLegalParams(params, bits) {
+ return params.map(function(param) {
+ var value = param.value || param;
+ if (isNumber(value.ident)) {
+ return getLegalLiterals(value.ident, bits);
+ } else if (value.intertype == 'structvalue') {
+ return getLegalStructuralParts(value).map(function(part) {
+ return { ident: part.ident, bits: part.type.substr(1) };
+ });
+ } else {
+ return getLegalVars(value.ident, bits);
}
+ });
+ }
+ // Uses the right factor to multiply line numbers by so that they fit in between
+ // the line[i] and the line after it
+ function interpLines(lines, i, toAdd) {
+ var prev = i >= 0 ? lines[i].lineNum : -1;
+ var next = (i < lines.length-1) ? lines[i+1].lineNum : (lines[i].lineNum + 0.5);
+ var factor = (next - prev)/(4*toAdd.length+3);
+ for (var k = 0; k < toAdd.length; k++) {
+ toAdd[k].lineNum = prev + ((k+1)*factor);
+ assert(k == 0 || toAdd[k].lineNum > toAdd[k-1].lineNum);
}
- function removeAndAdd(lines, i, toAdd) {
- var item = lines[i];
- interpLines(lines, i, toAdd);
- Array.prototype.splice.apply(lines, [i, 1].concat(toAdd));
- if (i > 0) assert(lines[i].lineNum > lines[i-1].lineNum);
- if (i + toAdd.length < lines.length) assert(lines[i + toAdd.length - 1].lineNum < lines[i + toAdd.length].lineNum);
- return toAdd.length;
- }
- function legalizeFunctionParameters(params) {
- var i = 0;
- while (i < params.length) {
- var param = params[i];
- if (param.intertype == 'value' && isIllegalType(param.type)) {
- var toAdd = getLegalVars(param.ident, getBits(param.type)).map(function(element) {
- return {
- intertype: 'value',
- type: 'i' + element.bits,
- ident: element.ident,
- byval: 0
- };
- });
- Array.prototype.splice.apply(params, [i, 1].concat(toAdd));
- i += toAdd.length;
- continue;
- } else if (param.intertype == 'structvalue') {
- // 'flatten' out the struct into scalars
- var toAdd = param.params;
- toAdd.forEach(function(param) {
- param.byval = 0;
- });
- Array.prototype.splice.apply(params, [i, 1].concat(toAdd));
- continue; // do not increment i; proceed to process the new params
- }
- i++;
+ }
+ function removeAndAdd(lines, i, toAdd) {
+ var item = lines[i];
+ interpLines(lines, i, toAdd);
+ Array.prototype.splice.apply(lines, [i, 1].concat(toAdd));
+ if (i > 0) assert(lines[i].lineNum > lines[i-1].lineNum);
+ if (i + toAdd.length < lines.length) assert(lines[i + toAdd.length - 1].lineNum < lines[i + toAdd.length].lineNum);
+ return toAdd.length;
+ }
+ function legalizeFunctionParameters(params) {
+ var i = 0;
+ while (i < params.length) {
+ var param = params[i];
+ if (param.intertype == 'value' && isIllegalType(param.type)) {
+ var toAdd = getLegalVars(param.ident, getBits(param.type)).map(function(element) {
+ return {
+ intertype: 'value',
+ type: 'i' + element.bits,
+ ident: element.ident,
+ byval: 0
+ };
+ });
+ Array.prototype.splice.apply(params, [i, 1].concat(toAdd));
+ i += toAdd.length;
+ continue;
+ } else if (param.intertype == 'structvalue') {
+ // 'flatten' out the struct into scalars
+ var toAdd = param.params;
+ toAdd.forEach(function(param) {
+ param.byval = 0;
+ });
+ Array.prototype.splice.apply(params, [i, 1].concat(toAdd));
+ continue; // do not increment i; proceed to process the new params
}
+ 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';
- }
+ }
+ 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);
- // Legalize lines in labels
- var tempId = 0;
- func.labels.forEach(function(label) {
- if (dcheck('legalizer')) dprint('zz legalizing: \n' + dump(label.lines));
- var i = 0, bits;
- while (i < label.lines.length) {
- var item = label.lines[i];
- var value = item;
- // Check if we need to legalize here, and do some trivial legalization along the way
- var isIllegal = false;
- walkInterdata(item, function(item) {
- if (item.intertype == 'getelementptr' || (item.intertype == 'call' && item.ident in LLVM.INTRINSICS_32)) {
- // Turn i64 args into i32
- for (var i = 0; i < item.params.length; i++) {
- if (item.params[i].type == 'i64') item.params[i].type = 'i32';
- }
- } else if (item.intertype == 'inttoptr') {
- var input = item.params[0];
- if (input.type == 'i64') input.type = 'i32'; // inttoptr can only care about 32 bits anyhow since pointers are 32-bit
- }
- if (isIllegalType(item.valueType) || isIllegalType(item.type)) {
- isIllegal = true;
- } else if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) {
- isIllegal = true; // storing an entire structure is illegal
- } else if (item.intertype == 'mathop' && item.op == 'trunc' && isIllegalType(item.params[1].ident)) { // trunc stores target value in second ident
- isIllegal = true;
+ }
+ data.functions.forEach(function(func) {
+ // Legalize function params
+ legalizeFunctionParameters(func.params);
+ // Legalize lines in labels
+ var tempId = 0;
+ func.labels.forEach(function(label) {
+ if (dcheck('legalizer')) dprint('zz legalizing: \n' + dump(label.lines));
+ var i = 0, bits;
+ while (i < label.lines.length) {
+ var item = label.lines[i];
+ var value = item;
+ // Check if we need to legalize here, and do some trivial legalization along the way
+ var isIllegal = false;
+ walkInterdata(item, function(item) {
+ if (item.intertype == 'getelementptr' || (item.intertype == 'call' && item.ident in LLVM.INTRINSICS_32)) {
+ // Turn i64 args into i32
+ for (var i = 0; i < item.params.length; i++) {
+ if (item.params[i].type == 'i64') item.params[i].type = 'i32';
}
- });
- if (!isIllegal) {
- //if (dcheck('legalizer')) dprint('no need to legalize \n' + dump(item));
- i++;
- continue;
+ } else if (item.intertype == 'inttoptr') {
+ var input = item.params[0];
+ if (input.type == 'i64') input.type = 'i32'; // inttoptr can only care about 32 bits anyhow since pointers are 32-bit
}
- // Unfold this line. If we unfolded, we need to return and process the lines we just
- // generated - they may need legalization too
- var unfolded = [];
- walkAndModifyInterdata(item, function(subItem) {
- // Unfold all non-value interitems that we can, and also unfold all numbers (doing the latter
- // makes it easier later since we can then assume illegal expressions are always variables
- // 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)))) {
- if (item.intertype == 'phi') {
- assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue' || subItem.intertype in PARSABLE_LLVM_FUNCTIONS, 'We can only unfold some expressions in phis');
- // we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi
- } else {
+ if (isIllegalType(item.valueType) || isIllegalType(item.type)) {
+ isIllegal = true;
+ } else if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) {
+ isIllegal = true; // storing an entire structure is illegal
+ } else if (item.intertype == 'mathop' && item.op == 'trunc' && isIllegalType(item.params[1].ident)) { // trunc stores target value in second ident
+ isIllegal = true;
+ }
+ });
+ if (!isIllegal) {
+ //if (dcheck('legalizer')) dprint('no need to legalize \n' + dump(item));
+ i++;
+ continue;
+ }
+ // Unfold this line. If we unfolded, we need to return and process the lines we just
+ // generated - they may need legalization too
+ var unfolded = [];
+ walkAndModifyInterdata(item, function(subItem) {
+ // Unfold all non-value interitems that we can, and also unfold all numbers (doing the latter
+ // makes it easier later since we can then assume illegal expressions are always variables
+ // 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)))) {
+ if (item.intertype == 'phi') {
+ assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue' || subItem.intertype in PARSABLE_LLVM_FUNCTIONS, 'We can only unfold some expressions 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 = '$$etemp$' + (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 = '$$etemp$' + (tempId++);
- subItem.assignTo = tempIdent;
- unfolded.unshift(subItem);
- fixUnfolded(subItem);
- return { intertype: 'value', ident: tempIdent, type: subItem.type };
+ unfolded.unshift({
+ assignTo: tempIdent,
+ intertype: 'value',
+ ident: switchLabel.value,
+ type: subItem.type
+ });
+ switchLabel.value = tempIdent;
}
- } else if (subItem.intertype == 'switch' && isIllegalType(subItem.type)) {
- subItem.switchLabels.forEach(function(switchLabel) {
- if (switchLabel.value[0] != '$') {
- var tempIdent = '$$etemp$' + (tempId++);
- unfolded.unshift({
- assignTo: tempIdent,
- intertype: 'value',
- ident: switchLabel.value,
- type: subItem.type
- });
- switchLabel.value = tempIdent;
- }
+ });
+ }
+ });
+ if (unfolded.length > 0) {
+ interpLines(label.lines, i-1, unfolded);
+ Array.prototype.splice.apply(label.lines, [i, 0].concat(unfolded));
+ continue; // remain at this index, to unfold newly generated lines
+ }
+ // This is an illegal-containing line, and it is unfolded. Legalize it now
+ dprint('legalizer', 'Legalizing ' + item.intertype + ' at line ' + item.lineNum);
+ var finalizer = null;
+ switch (item.intertype) {
+ case 'store': {
+ var toAdd = [];
+ bits = getBits(item.valueType);
+ var elements = getLegalParams([item.value], bits)[0];
+ var j = 0;
+ elements.forEach(function(element) {
+ var tempVar = '$st$' + (tempId++) + '$' + j;
+ toAdd.push({
+ intertype: 'getelementptr',
+ assignTo: tempVar,
+ 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' }
+ ],
});
- }
- });
- if (unfolded.length > 0) {
- interpLines(label.lines, i-1, unfolded);
- Array.prototype.splice.apply(label.lines, [i, 0].concat(unfolded));
- continue; // remain at this index, to unfold newly generated lines
+ var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits
+ toAdd.push({
+ 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,
+ });
+ j++;
+ });
+ Types.needAnalysis['[0 x i32]'] = 0;
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
}
- // This is an illegal-containing line, and it is unfolded. Legalize it now
- dprint('legalizer', 'Legalizing ' + item.intertype + ' at line ' + item.lineNum);
- var finalizer = null;
- switch (item.intertype) {
- case 'store': {
- var toAdd = [];
- bits = getBits(item.valueType);
- var elements = getLegalParams([item.value], bits)[0];
- var j = 0;
- elements.forEach(function(element) {
- var tempVar = '$st$' + (tempId++) + '$' + j;
- toAdd.push({
- intertype: 'getelementptr',
- assignTo: tempVar,
- 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' }
- ],
- });
- var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits
+ // call, return: Return the first 32 bits, the rest are in temp
+ case 'call': {
+ var toAdd = [value];
+ // legalize parameters
+ legalizeFunctionParameters(value.params);
+ // legalize return value, if any
+ var returnType = getReturnType(item.type);
+ if (value.assignTo && isIllegalType(returnType)) {
+ bits = getBits(returnType);
+ var elements = getLegalVars(item.assignTo, bits);
+ // legalize return value
+ value.assignTo = elements[0].ident;
+ for (var j = 1; j < elements.length; j++) {
+ var element = elements[j];
toAdd.push({
- 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,
+ intertype: 'value',
+ assignTo: element.ident,
+ type: element.bits,
+ ident: 'tempRet' + (j - 1)
});
- j++;
- });
- Types.needAnalysis['[0 x i32]'] = 0;
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
- }
- // call, return: Return the first 32 bits, the rest are in temp
- case 'call': {
- var toAdd = [value];
- // legalize parameters
- legalizeFunctionParameters(value.params);
- // legalize return value, if any
- var returnType = getReturnType(item.type);
- if (value.assignTo && isIllegalType(returnType)) {
- bits = getBits(returnType);
- var elements = getLegalVars(item.assignTo, bits);
- // legalize return value
- value.assignTo = elements[0].ident;
- for (var j = 1; j < elements.length; j++) {
- var element = elements[j];
- toAdd.push({
- intertype: 'value',
- assignTo: element.ident,
- type: element.bits,
- ident: 'tempRet' + (j - 1)
- });
- assert(j<10); // TODO: dynamically create more than 10 tempRet-s
- }
+ assert(j<10); // TODO: dynamically create more than 10 tempRet-s
}
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
}
- case 'landingpad': {
- // not much to legalize
- i++;
- continue;
- }
- case 'return': {
- bits = getBits(item.type);
- var elements = getLegalVars(item.value.ident, bits);
- item.value.ident = '(';
- for (var j = 1; j < elements.length; j++) {
- item.value.ident += 'tempRet' + (j-1) + '=' + elements[j].ident + ',';
- }
- item.value.ident += elements[0].ident + ')';
- i++;
- continue;
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'landingpad': {
+ // not much to legalize
+ i++;
+ continue;
+ }
+ case 'return': {
+ bits = getBits(item.type);
+ var elements = getLegalVars(item.value.ident, bits);
+ item.value.ident = '(';
+ for (var j = 1; j < elements.length; j++) {
+ item.value.ident += 'tempRet' + (j-1) + '=' + elements[j].ident + ',';
}
- 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;
+ item.value.ident += elements[0].ident + ')';
+ 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);
+ var values = getLegalLiterals(item.ident, bits);
+ var j = 0;
+ var toAdd = elements.map(function(element) {
+ return {
+ intertype: 'value',
+ assignTo: element.ident,
+ type: 'i' + bits,
+ ident: values[j++].ident
+ };
+ });
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'structvalue': {
+ bits = getBits(value.type);
+ var elements = getLegalVars(item.assignTo, bits);
+ var toAdd = [];
+ for (var j = 0; j < item.params.length; j++) {
+ toAdd[j] = {
+ intertype: 'value',
+ assignTo: elements[j].ident,
+ type: 'i32',
+ ident: item.params[j].ident
+ };
}
- case 'value': {
- bits = getBits(value.type);
- var elements = getLegalVars(item.assignTo, bits);
- var values = getLegalLiterals(item.ident, bits);
- var j = 0;
- var toAdd = elements.map(function(element) {
- return {
- intertype: 'value',
- assignTo: element.ident,
- type: 'i' + bits,
- ident: values[j++].ident
- };
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'load': {
+ bits = getBits(value.valueType);
+ var elements = getLegalVars(item.assignTo, bits);
+ var j = 0;
+ var toAdd = [];
+ elements.forEach(function(element) {
+ var tempVar = '$ld$' + (tempId++) + '$' + j;
+ toAdd.push({
+ intertype: 'getelementptr',
+ assignTo: tempVar,
+ 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' }
+ ]
});
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
- }
- case 'structvalue': {
- bits = getBits(value.type);
- var elements = getLegalVars(item.assignTo, bits);
- var toAdd = [];
- for (var j = 0; j < item.params.length; j++) {
- toAdd[j] = {
- intertype: 'value',
- assignTo: elements[j].ident,
- type: 'i32',
- ident: item.params[j].ident
- };
- }
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
- }
- case 'load': {
- bits = getBits(value.valueType);
- var elements = getLegalVars(item.assignTo, bits);
- var j = 0;
- var toAdd = [];
- elements.forEach(function(element) {
- var tempVar = '$ld$' + (tempId++) + '$' + j;
- toAdd.push({
- intertype: 'getelementptr',
- assignTo: tempVar,
- 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' }
- ]
- });
- var newItem = {
- intertype: 'load',
+ var newItem = {
+ intertype: 'load',
+ assignTo: element.ident,
+ pointerType: 'i32*',
+ valueType: 'i32',
+ type: 'i32',
+ pointer: { intertype: 'value', ident: tempVar, type: 'i32*' },
+ ident: tempVar,
+ align: value.align
+ };
+ var newItem2 = null;
+ // The last one may be smaller than 32 bits
+ if (element.bits < 32) {
+ newItem.assignTo += '$preadd$';
+ newItem2 = {
+ intertype: 'mathop',
+ op: 'and',
assignTo: element.ident,
- pointerType: 'i32*',
- valueType: 'i32',
type: 'i32',
- pointer: { intertype: 'value', ident: tempVar, type: 'i32*' },
- ident: tempVar,
- align: value.align
- };
- var newItem2 = null;
- // The last one may be smaller than 32 bits
- if (element.bits < 32) {
- newItem.assignTo += '$preadd$';
- newItem2 = {
- intertype: 'mathop',
- op: 'and',
- assignTo: element.ident,
+ params: [{
+ intertype: 'value',
type: 'i32',
- params: [{
- intertype: 'value',
- type: 'i32',
- ident: newItem.assignTo
- }, {
- intertype: 'value',
- type: 'i32',
- ident: (0xffffffff >>> (32 - element.bits)).toString()
- }],
- };
- }
- toAdd.push(newItem);
- if (newItem2) toAdd.push(newItem2);
- j++;
- });
- Types.needAnalysis['[0 x i32]'] = 0;
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
- }
- case 'phi': {
- bits = getBits(value.type);
- var toAdd = [];
- var elements = getLegalVars(item.assignTo, bits);
- var j = 0;
- var values = getLegalParams(value.params, bits);
- elements.forEach(function(element) {
- var k = 0;
- toAdd.push({
- intertype: 'phi',
- assignTo: element.ident,
- type: 'i' + element.bits,
- params: value.params.map(function(param) {
- return {
- intertype: 'phiparam',
- label: param.label,
- value: {
- intertype: 'value',
- ident: values[k++][j].ident,
- type: 'i' + element.bits,
- }
- };
- })
- });
- j++;
- });
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
- }
- case 'switch': {
- i++;
- continue; // special case, handled in makeComparison
- }
- case 'va_arg': {
- assert(value.type == 'i64');
- assert(value.value.type == 'i32*', value.value.type);
- i += removeAndAdd(label.lines, i, range(2).map(function(x) {
- return {
- intertype: 'va_arg',
- assignTo: value.assignTo + '$' + x,
- type: 'i32',
- value: {
+ ident: newItem.assignTo
+ }, {
intertype: 'value',
- ident: value.value.ident, // We read twice from the same i32* var, incrementing // + '$' + x,
- type: 'i32*'
- }
+ type: 'i32',
+ ident: (0xffffffff >>> (32 - element.bits)).toString()
+ }],
};
- }));
- continue;
- }
- case 'extractvalue': { // XXX we assume 32-bit alignment in extractvalue/insertvalue,
- // but in theory they can run on packed structs too (see use getStructuralTypePartBits)
- // potentially legalize the actual extracted value too if it is >32 bits, not just the extraction in general
- var index = item.indexes[0][0].text;
- var parts = getStructureTypeParts(item.type);
- var indexedType = parts[index];
- var targetBits = getBits(indexedType);
- var sourceBits = getBits(item.type);
- var elements = getLegalVars(item.assignTo, targetBits, true); // possibly illegal
- var sourceElements = getLegalVars(item.ident, sourceBits); // definitely illegal
- var toAdd = [];
- var sourceIndex = 0;
- for (var partIndex = 0; partIndex < parts.length; partIndex++) {
- if (partIndex == index) {
- for (var j = 0; j < elements.length; j++) {
- toAdd.push({
- intertype: 'value',
- assignTo: elements[j].ident,
- type: 'i' + elements[j].bits,
- ident: sourceElements[sourceIndex+j].ident
- });
- }
- break;
- }
- sourceIndex += getStructuralTypePartBits(parts[partIndex])/32;
}
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
- }
- case 'insertvalue': {
- var index = item.indexes[0][0].text; // the modified index
- var parts = getStructureTypeParts(item.type);
- var indexedType = parts[index];
- var indexBits = getBits(indexedType);
- var bits = getBits(item.type); // source and target
- bits = getBits(value.type);
- var toAdd = [];
- var elements = getLegalVars(item.assignTo, bits);
- var sourceElements = getLegalVars(item.ident, bits);
- var indexElements = getLegalVars(item.value.ident, indexBits, true); // possibly legal
- var sourceIndex = 0;
- for (var partIndex = 0; partIndex < parts.length; partIndex++) {
- var currNum = getStructuralTypePartBits(parts[partIndex])/32;
- for (var j = 0; j < currNum; j++) {
+ toAdd.push(newItem);
+ if (newItem2) toAdd.push(newItem2);
+ j++;
+ });
+ Types.needAnalysis['[0 x i32]'] = 0;
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'phi': {
+ bits = getBits(value.type);
+ var toAdd = [];
+ var elements = getLegalVars(item.assignTo, bits);
+ var j = 0;
+ var values = getLegalParams(value.params, bits);
+ elements.forEach(function(element) {
+ var k = 0;
+ toAdd.push({
+ intertype: 'phi',
+ assignTo: element.ident,
+ type: 'i' + element.bits,
+ params: value.params.map(function(param) {
+ return {
+ intertype: 'phiparam',
+ label: param.label,
+ value: {
+ intertype: 'value',
+ ident: values[k++][j].ident,
+ type: 'i' + element.bits,
+ }
+ };
+ })
+ });
+ j++;
+ });
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'switch': {
+ i++;
+ continue; // special case, handled in makeComparison
+ }
+ case 'va_arg': {
+ assert(value.type == 'i64');
+ assert(value.value.type == 'i32*', value.value.type);
+ i += removeAndAdd(label.lines, i, range(2).map(function(x) {
+ return {
+ intertype: 'va_arg',
+ assignTo: value.assignTo + '$' + x,
+ type: 'i32',
+ value: {
+ intertype: 'value',
+ ident: value.value.ident, // We read twice from the same i32* var, incrementing // + '$' + x,
+ type: 'i32*'
+ }
+ };
+ }));
+ continue;
+ }
+ case 'extractvalue': { // XXX we assume 32-bit alignment in extractvalue/insertvalue,
+ // but in theory they can run on packed structs too (see use getStructuralTypePartBits)
+ // potentially legalize the actual extracted value too if it is >32 bits, not just the extraction in general
+ var index = item.indexes[0][0].text;
+ var parts = getStructureTypeParts(item.type);
+ var indexedType = parts[index];
+ var targetBits = getBits(indexedType);
+ var sourceBits = getBits(item.type);
+ var elements = getLegalVars(item.assignTo, targetBits, true); // possibly illegal
+ var sourceElements = getLegalVars(item.ident, sourceBits); // definitely illegal
+ var toAdd = [];
+ var sourceIndex = 0;
+ for (var partIndex = 0; partIndex < parts.length; partIndex++) {
+ if (partIndex == index) {
+ for (var j = 0; j < elements.length; j++) {
toAdd.push({
intertype: 'value',
- assignTo: elements[sourceIndex+j].ident,
- type: 'i' + elements[sourceIndex+j].bits,
- ident: partIndex == index ? indexElements[j].ident : sourceElements[sourceIndex+j].ident
+ assignTo: elements[j].ident,
+ type: 'i' + elements[j].bits,
+ ident: sourceElements[sourceIndex+j].ident
});
}
- sourceIndex += currNum;
+ break;
}
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
+ sourceIndex += getStructuralTypePartBits(parts[partIndex])/32;
}
- case 'bitcast': {
- var inType = item.type2;
- var outType = item.type;
- if ((inType in Runtime.INT_TYPES && outType in Runtime.FLOAT_TYPES) ||
- (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES)) {
- i++;
- continue; // special case, handled in processMathop
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'insertvalue': {
+ var index = item.indexes[0][0].text; // the modified index
+ var parts = getStructureTypeParts(item.type);
+ var indexedType = parts[index];
+ var indexBits = getBits(indexedType);
+ var bits = getBits(item.type); // source and target
+ bits = getBits(value.type);
+ var toAdd = [];
+ var elements = getLegalVars(item.assignTo, bits);
+ var sourceElements = getLegalVars(item.ident, bits);
+ var indexElements = getLegalVars(item.value.ident, indexBits, true); // possibly legal
+ var sourceIndex = 0;
+ for (var partIndex = 0; partIndex < parts.length; partIndex++) {
+ var currNum = getStructuralTypePartBits(parts[partIndex])/32;
+ for (var j = 0; j < currNum; j++) {
+ toAdd.push({
+ intertype: 'value',
+ assignTo: elements[sourceIndex+j].ident,
+ type: 'i' + elements[sourceIndex+j].bits,
+ ident: partIndex == index ? indexElements[j].ident : sourceElements[sourceIndex+j].ident
+ });
}
- // fall through
+ sourceIndex += currNum;
}
- 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,
- variant: item.variant,
- type: item.type,
- params: item.params
- };
- // fall through
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'bitcast': {
+ var inType = item.type2;
+ var outType = item.type;
+ if ((inType in Runtime.INT_TYPES && outType in Runtime.FLOAT_TYPES) ||
+ (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES)) {
+ i++;
+ continue; // special case, handled in processMathop
}
- case 'mathop': {
- var toAdd = [];
- 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;
- var processor = null;
- var signed = false;
- switch (value.op) {
- case 'ashr': {
- signed = true;
- // fall through
- }
- case 'lshr': {
- shifts = parseInt(value.params[1].ident);
- break;
- }
- case 'shl': {
- shifts = -parseInt(value.params[1].ident);
- break;
- }
- case 'sext': {
- signed = true;
- // fall through
- }
- case 'trunc': case 'zext': case 'ptrtoint': {
- targetBits = getBits(value.params[1] ? value.params[1].ident : value.type);
- break;
- }
- case 'inttoptr': {
- targetBits = 32;
- break;
- }
- case 'bitcast': {
- if (!sourceBits) {
- // we can be asked to bitcast doubles or such to integers, handle that as best we can (if it's a double that
- // was an x86_fp80, this code will likely break when called)
- sourceBits = targetBits = Runtime.getNativeTypeSize(value.params[0].type);
- warn('legalizing non-integer bitcast on ll #' + item.lineNum);
- }
- break;
- }
- case 'select': {
- sourceBits = targetBits = getBits(value.params[1].type);
- var params = getLegalParams(value.params.slice(1), sourceBits);
- processor = function(result, j) {
- return {
- intertype: 'mathop',
- op: 'select',
- type: 'i' + params[0][j].bits,
- params: [
- value.params[0],
- { intertype: 'value', ident: params[0][j].ident, type: 'i' + params[0][j].bits },
- { intertype: 'value', ident: params[1][j].ident, type: 'i' + params[1][j].bits }
- ]
- };
- };
- break;
- }
- case 'or': case 'and': case 'xor': case 'icmp': {
- var otherElements = getLegalVars(value.params[1].ident, sourceBits);
- processor = function(result, j) {
- return {
- intertype: 'mathop',
- op: value.op,
- variant: value.variant,
- type: 'i' + otherElements[j].bits,
- params: [
- result,
- { intertype: 'value', ident: otherElements[j].ident, type: 'i' + otherElements[j].bits }
- ]
- };
- };
- if (value.op == 'icmp') {
- if (sourceBits == 64) { // handle the i64 case in processMathOp, where we handle full i64 math
- i++;
- continue;
- }
- finalizer = function() {
- var ident = '';
- for (var i = 0; i < targetElements.length; i++) {
- if (i > 0) {
- switch(value.variant) {
- case 'eq': ident += '&'; break;
- case 'ne': ident += '|'; break;
- default: throw 'unhandleable illegal icmp: ' + value.variant;
- }
- }
- ident += targetElements[i].ident;
- }
- return {
- intertype: 'value',
- ident: ident,
- type: 'rawJS',
- assignTo: item.assignTo
- };
- }
- }
- break;
- }
- case 'add': case 'sub': case 'sdiv': case 'udiv': case 'mul': case 'urem': case 'srem': {
- if (sourceBits < 32) {
- // when we add illegal types like i24, we must work on the singleton chunks
- item.assignTo += '$0';
- item.params[0].ident += '$0';
- item.params[1].ident += '$0';
- }
- // fall through
- }
- case 'uitofp': case 'sitofp': case 'fptosi': case 'fptoui': {
- // We cannot do these in parallel chunks of 32-bit operations. We will handle these in processMathop
- i++;
- continue;
- }
- default: throw 'Invalid mathop for legalization: ' + [value.op, item.lineNum, dump(item)];
+ // fall through
+ }
+ 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,
+ variant: item.variant,
+ type: item.type,
+ params: item.params
+ };
+ // fall through
+ }
+ case 'mathop': {
+ var toAdd = [];
+ 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;
+ var processor = null;
+ var signed = false;
+ switch (value.op) {
+ case 'ashr': {
+ signed = true;
+ // fall through
}
- // Do the legalization
- var sourceElements = getLegalVars(value.params[0].ident, sourceBits, true);
- 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');
- assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts');
- Types.preciseI64MathUsed = 1;
- value.intertype = 'value';
- value.ident = 'var ' + value.assignTo + '$0 = ' +
- asmCoercion('_bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' +
- asmCoercion(sourceElements[0].ident, 'i32') + ',' +
- asmCoercion(sourceElements[1].ident, 'i32') + ',' +
- asmCoercion(value.params[1].ident + '$0', 'i32') + ')', 'i32'
- ) + ';' +
- 'var ' + value.assignTo + '$1 = tempRet0;';
- value.assignTo = null;
- i++;
- continue;
+ case 'lshr': {
+ shifts = parseInt(value.params[1].ident);
+ break;
}
- var targetElements = getLegalVars(item.assignTo, 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);
- if (signed) {
- var signedFill = '(' + makeSignOp(sourceElements[sourceElements.length-1].ident, 'i' + sourceElements[sourceElements.length-1].bits, 're', 1, 1) + ' < 0 ? -1 : 0)';
- var signedKeepAlive = { intertype: 'value', ident: sourceElements[sourceElements.length-1].ident, type: 'i32' };
+ case 'shl': {
+ shifts = -parseInt(value.params[1].ident);
+ break;
}
- for (var j = 0; j < targetElements.length; j++) {
- var result = {
- intertype: 'value',
- ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : (signed ? signedFill : '0'),
- params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null],
- type: 'i32',
- };
- if (j == 0 && sourceBits < 32) {
- // zext sign correction
- result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1);
+ case 'sext': {
+ signed = true;
+ // fall through
+ }
+ case 'trunc': case 'zext': case 'ptrtoint': {
+ targetBits = getBits(value.params[1] ? value.params[1].ident : value.type);
+ break;
+ }
+ case 'inttoptr': {
+ targetBits = 32;
+ break;
+ }
+ case 'bitcast': {
+ if (!sourceBits) {
+ // we can be asked to bitcast doubles or such to integers, handle that as best we can (if it's a double that
+ // was an x86_fp80, this code will likely break when called)
+ sourceBits = targetBits = Runtime.getNativeTypeSize(value.params[0].type);
+ warn('legalizing non-integer bitcast on ll #' + item.lineNum);
}
- 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'),
- params: [(signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null],
- type: 'i32',
- };
- other = {
+ break;
+ }
+ case 'select': {
+ sourceBits = targetBits = getBits(value.params[1].type);
+ var params = getLegalParams(value.params.slice(1), sourceBits);
+ processor = function(result, j) {
+ return {
intertype: 'mathop',
- op: shiftOp,
- type: 'i32',
+ op: 'select',
+ type: 'i' + params[0][j].bits,
params: [
- other,
- { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' }
+ value.params[0],
+ { intertype: 'value', ident: params[0][j].ident, type: 'i' + params[0][j].bits },
+ { intertype: 'value', ident: params[1][j].ident, type: 'i' + params[1][j].bits }
]
};
- result = {
+ };
+ break;
+ }
+ case 'or': case 'and': case 'xor': case 'icmp': {
+ var otherElements = getLegalVars(value.params[1].ident, sourceBits);
+ processor = function(result, j) {
+ return {
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',
+ op: value.op,
+ variant: value.variant,
+ type: 'i' + otherElements[j].bits,
params: [
result,
- { intertype: 'value', ident: fraction.toString(), type: 'i32' }
+ { intertype: 'value', ident: otherElements[j].ident, type: 'i' + otherElements[j].bits }
]
};
- result = {
- intertype: 'mathop',
- op: 'or',
- type: 'i32',
- params: [
- result,
- other
- ]
+ };
+ if (value.op == 'icmp') {
+ if (sourceBits == 64) { // handle the i64 case in processMathOp, where we handle full i64 math
+ i++;
+ continue;
}
- }
- 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',
- params: [
- result,
- { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' }
- ]
+ finalizer = function() {
+ var ident = '';
+ for (var i = 0; i < targetElements.length; i++) {
+ if (i > 0) {
+ switch(value.variant) {
+ case 'eq': ident += '&'; break;
+ case 'ne': ident += '|'; break;
+ default: throw 'unhandleable illegal icmp: ' + value.variant;
+ }
+ }
+ ident += targetElements[i].ident;
+ }
+ return {
+ intertype: 'value',
+ ident: ident,
+ type: 'rawJS',
+ assignTo: item.assignTo
+ };
}
}
- if (processor) {
- result = processor(result, j);
+ break;
+ }
+ case 'add': case 'sub': case 'sdiv': case 'udiv': case 'mul': case 'urem': case 'srem': {
+ if (sourceBits < 32) {
+ // when we add illegal types like i24, we must work on the singleton chunks
+ item.assignTo += '$0';
+ item.params[0].ident += '$0';
+ item.params[1].ident += '$0';
}
- result.assignTo = targetElements[j].ident;
- toAdd.push(result);
+ // fall through
}
- if (targetBits <= 32) {
- // We are generating a normal legal type here
- legalValue = {
+ case 'uitofp': case 'sitofp': case 'fptosi': case 'fptoui': {
+ // We cannot do these in parallel chunks of 32-bit operations. We will handle these in processMathop
+ i++;
+ continue;
+ }
+ default: throw 'Invalid mathop for legalization: ' + [value.op, item.lineNum, dump(item)];
+ }
+ // Do the legalization
+ var sourceElements = getLegalVars(value.params[0].ident, sourceBits, true);
+ 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');
+ assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts');
+ Types.preciseI64MathUsed = 1;
+ value.intertype = 'value';
+ value.ident = 'var ' + value.assignTo + '$0 = ' +
+ asmCoercion('_bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' +
+ asmCoercion(sourceElements[0].ident, 'i32') + ',' +
+ asmCoercion(sourceElements[1].ident, 'i32') + ',' +
+ asmCoercion(value.params[1].ident + '$0', 'i32') + ')', 'i32'
+ ) + ';' +
+ 'var ' + value.assignTo + '$1 = tempRet0;';
+ value.assignTo = null;
+ i++;
+ continue;
+ }
+ var targetElements = getLegalVars(item.assignTo, 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);
+ if (signed) {
+ var signedFill = '(' + makeSignOp(sourceElements[sourceElements.length-1].ident, 'i' + sourceElements[sourceElements.length-1].bits, 're', 1, 1) + ' < 0 ? -1 : 0)';
+ var signedKeepAlive = { intertype: 'value', ident: sourceElements[sourceElements.length-1].ident, type: 'i32' };
+ }
+ for (var j = 0; j < targetElements.length; j++) {
+ var result = {
+ intertype: 'value',
+ ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : (signed ? signedFill : '0'),
+ params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null],
+ type: 'i32',
+ };
+ if (j == 0 && sourceBits < 32) {
+ // zext sign correction
+ result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1);
+ }
+ if (fraction != 0) {
+ var other = {
intertype: 'value',
- ident: targetElements[0].ident + (targetBits < 32 ? '&' + (Math.pow(2, targetBits)-1) : ''),
- type: 'rawJS'
+ ident: (j + sign + whole >= 0 && j + sign + whole < sourceElements.length) ? sourceElements[j + sign + whole].ident : (signed ? signedFill : '0'),
+ params: [(signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null],
+ type: 'i32',
+ };
+ other = {
+ intertype: 'mathop',
+ op: shiftOp,
+ type: 'i32',
+ params: [
+ other,
+ { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' }
+ ]
};
- legalValue.assignTo = item.assignTo;
- toAdd.push(legalValue);
- } else if (finalizer) {
- toAdd.push(finalizer());
+ 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',
+ params: [
+ result,
+ { intertype: 'value', ident: fraction.toString(), type: 'i32' }
+ ]
+ };
+ result = {
+ intertype: 'mathop',
+ op: 'or',
+ type: 'i32',
+ params: [
+ result,
+ other
+ ]
+ }
}
- i += removeAndAdd(label.lines, i, toAdd);
- continue;
+ 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',
+ params: [
+ result,
+ { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' }
+ ]
+ }
+ }
+ if (processor) {
+ result = processor(result, j);
+ }
+ result.assignTo = targetElements[j].ident;
+ toAdd.push(result);
}
+ if (targetBits <= 32) {
+ // We are generating a normal legal type here
+ legalValue = {
+ intertype: 'value',
+ ident: targetElements[0].ident + (targetBits < 32 ? '&' + (Math.pow(2, targetBits)-1) : ''),
+ type: 'rawJS'
+ };
+ legalValue.assignTo = item.assignTo;
+ toAdd.push(legalValue);
+ } else if (finalizer) {
+ toAdd.push(finalizer());
+ }
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
}
- assert(0, 'Could not legalize illegal line: ' + [item.lineNum, dump(item)]);
}
- if (dcheck('legalizer')) dprint('zz legalized: \n' + dump(label.lines));
- });
- });
- }
-
- // 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);
+ assert(0, 'Could not legalize illegal line: ' + [item.lineNum, dump(item)]);
+ }
+ if (dcheck('legalizer')) dprint('zz legalized: \n' + dump(label.lines));
});
});
- this.forwardItem(data, 'Typevestigator');
}
- });
- function addTypeInternal(type, data) {
+ // 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);
+ });
+ });
+ }
+
+ function addTypeInternal(type) {
if (type.length == 1) return;
if (Types.types[type]) return;
if (['internal', 'hidden', 'inbounds', 'void'].indexOf(type) != -1) return;
@@ -927,8 +911,9 @@ function analyzer(data, sidePass) {
// to look at the underlying type - it was not defined explicitly
// anywhere else.
var nonPointing = removeAllPointing(type);
+ if (Types.types[nonPointing]) return;
var check = /^\[(\d+)\ x\ (.*)\]$/.exec(nonPointing);
- if (check && !Types.types[nonPointing]) {
+ if (check) {
var num = parseInt(check[1]);
num = Math.max(num, 1); // [0 x something] is used not for allocations and such of course, but
// for indexing - for an |array of unknown length|, basically. So we
@@ -936,7 +921,7 @@ function analyzer(data, sidePass) {
// check that we never allocate with this (either as a child structure
// in the analyzer, or in calcSize in alloca).
var subType = check[2];
- addTypeInternal(subType, data); // needed for anonymous structure definitions (see below)
+ addTypeInternal(subType); // needed for anonymous structure definitions (see below)
// Huge structural types are represented very inefficiently, both here and in generated JS. Best to avoid them - for example static char x[10*1024*1024]; is bad, while static char *x = malloc(10*1024*1024) is fine.
if (num >= 10*1024*1024) warnOnce('warning: very large fixed-size structural type: ' + type + ' - can you reduce it? (compilation may be slow)');
@@ -945,6 +930,7 @@ function analyzer(data, sidePass) {
fields: range(num).map(function() { return subType }),
lineNum: '?'
};
+ newTypes[nonPointing] = 1;
// Also add a |[0 x type]| type
var zerod = '[0 x ' + subType + ']';
if (!Types.types[zerod]) {
@@ -953,6 +939,7 @@ function analyzer(data, sidePass) {
fields: [subType, subType], // Two, so we get the flatFactor right. We care about the flatFactor, not the size here
lineNum: '?'
};
+ newTypes[zerod] = 1;
}
return;
}
@@ -983,6 +970,7 @@ function analyzer(data, sidePass) {
packed: packed,
lineNum: '?'
};
+ newTypes[type] = 1;
return;
}
@@ -994,251 +982,243 @@ function analyzer(data, sidePass) {
flatSize: 1,
lineNum: '?'
};
+ newTypes[type] = 1;
}
- function addType(type, data) {
- addTypeInternal(type, data);
+ function addType(type) {
+ addTypeInternal(type);
if (QUANTUM_SIZE === 1) {
Types.flipTypes();
- addTypeInternal(type, data);
+ addTypeInternal(type);
Types.flipTypes();
}
}
// Typevestigator
- substrate.addActor('Typevestigator', {
- processItem: function(data) {
- if (sidePass) { // Do not investigate in the main pass - it is only valid to start to do so in the first side pass,
- // which handles type definitions, and later. Doing so before the first side pass will result in
- // making bad guesses about types which are actually defined
- for (var type in Types.needAnalysis) {
- if (type) addType(type, data);
- }
- Types.needAnalysis = {};
+ function typevestigator() {
+ if (sidePass) { // Do not investigate in the main pass - it is only valid to start to do so in the first side pass,
+ // which handles type definitions, and later. Doing so before the first side pass will result in
+ // making bad guesses about types which are actually defined
+ for (var type in Types.needAnalysis) {
+ if (type) addType(type);
}
- this.forwardItem(data, 'Typeanalyzer');
+ Types.needAnalysis = {};
}
- });
+ }
// Type analyzer
- substrate.addActor('Typeanalyzer', {
- processItem: function analyzeTypes(item, fatTypes) {
- var types = Types.types;
-
- // 'fields' is the raw list of LLVM fields. However, we embed
- // child structures into parent structures, basically like C.
- // So { int, { int, int }, int } would be represented as
- // an Array of 4 ints. getelementptr on the parent would take
- // values 0, 1, 2, where 2 is the entire middle structure.
- // We also need to be careful with getelementptr to child
- // structures - we return a pointer to the same slab, just
- // a different offset. Likewise, need to be careful for
- // getelementptr of 2 (the last int) - it's real index is 4.
- // The benefit of this approach is inheritance -
- // { { ancestor } , etc. } = descendant
- // In this case it is easy to bitcast ancestor to descendant
- // pointers - nothing needs to be done. If the ancestor were
- // a new slab, it would need some pointer to the outer one
- // for casting in that direction.
- // TODO: bitcasts of non-inheritance cases of embedding (not at start)
- var more = true;
- while (more) {
- more = false;
- for (var typeName in types) {
- var type = types[typeName];
- if (type.flatIndexes) continue;
- var ready = true;
- type.fields.forEach(function(field) {
- if (isStructType(field)) {
- if (!types[field]) {
- addType(field, item);
+ function analyzeTypes(fatTypes) {
+ var types = Types.types;
+
+ // 'fields' is the raw list of LLVM fields. However, we embed
+ // child structures into parent structures, basically like C.
+ // So { int, { int, int }, int } would be represented as
+ // an Array of 4 ints. getelementptr on the parent would take
+ // values 0, 1, 2, where 2 is the entire middle structure.
+ // We also need to be careful with getelementptr to child
+ // structures - we return a pointer to the same slab, just
+ // a different offset. Likewise, need to be careful for
+ // getelementptr of 2 (the last int) - it's real index is 4.
+ // The benefit of this approach is inheritance -
+ // { { ancestor } , etc. } = descendant
+ // In this case it is easy to bitcast ancestor to descendant
+ // pointers - nothing needs to be done. If the ancestor were
+ // a new slab, it would need some pointer to the outer one
+ // for casting in that direction.
+ // TODO: bitcasts of non-inheritance cases of embedding (not at start)
+ var more = true;
+ while (more) {
+ more = false;
+ for (var typeName in newTypes) {
+ var type = types[typeName];
+ if (type.flatIndexes) continue;
+ var ready = true;
+ type.fields.forEach(function(field) {
+ if (isStructType(field)) {
+ if (!types[field]) {
+ addType(field);
+ ready = false;
+ } else {
+ if (!types[field].flatIndexes) {
+ newTypes[field] = 1;
ready = false;
- } else {
- if (!types[field].flatIndexes) {
- ready = false;
- }
}
}
- });
- if (!ready) {
- more = true;
- continue;
}
-
- Runtime.calculateStructAlignment(type);
-
- if (dcheck('types')) dprint('type (fat=' + !!fatTypes + '): ' + type.name_ + ' : ' + JSON.stringify(type.fields));
- if (dcheck('types')) dprint(' has final size of ' + type.flatSize + ', flatting: ' + type.needsFlattening + ' ? ' + (type.flatFactor ? type.flatFactor : JSON.stringify(type.flatIndexes)));
+ });
+ if (!ready) {
+ more = true;
+ continue;
}
- }
- if (QUANTUM_SIZE === 1 && !fatTypes) {
- Types.flipTypes();
- // Fake a quantum size of 4 for fat types. TODO: Might want non-4 for some reason?
- var trueQuantumSize = QUANTUM_SIZE;
- Runtime.QUANTUM_SIZE = 4;
- analyzeTypes(item, true);
- Runtime.QUANTUM_SIZE = trueQuantumSize;
- Types.flipTypes();
- }
+ Runtime.calculateStructAlignment(type);
- if (!fatTypes) {
- this.forwardItem(item, 'VariableAnalyzer');
+ if (dcheck('types')) dprint('type (fat=' + !!fatTypes + '): ' + type.name_ + ' : ' + JSON.stringify(type.fields));
+ if (dcheck('types')) dprint(' has final size of ' + type.flatSize + ', flatting: ' + type.needsFlattening + ' ? ' + (type.flatFactor ? type.flatFactor : JSON.stringify(type.flatIndexes)));
}
}
- });
+
+ if (QUANTUM_SIZE === 1 && !fatTypes) {
+ Types.flipTypes();
+ // Fake a quantum size of 4 for fat types. TODO: Might want non-4 for some reason?
+ var trueQuantumSize = QUANTUM_SIZE;
+ Runtime.QUANTUM_SIZE = 4;
+ analyzeTypes(true);
+ Runtime.QUANTUM_SIZE = trueQuantumSize;
+ Types.flipTypes();
+ }
+
+ newTypes = null;
+ }
// Variable analyzer
- substrate.addActor('VariableAnalyzer', {
- processItem: function(item) {
- // Globals
-
- var old = item.globalVariables;
- item.globalVariables = {};
- old.forEach(function(variable) {
- variable.impl = 'emulated'; // All global variables are emulated, for now. Consider optimizing later if useful
- item.globalVariables[variable.ident] = variable;
+ function variableAnalyzer() {
+ // Globals
+
+ var old = item.globalVariables;
+ item.globalVariables = {};
+ old.forEach(function(variable) {
+ variable.impl = 'emulated'; // All global variables are emulated, for now. Consider optimizing later if useful
+ item.globalVariables[variable.ident] = variable;
+ });
+
+ // Function locals
+
+ item.functions.forEach(function(func) {
+ func.variables = {};
+
+ // LLVM is SSA, so we always have a single assignment/write. We care about
+ // the reads/other uses.
+
+ // Function parameters
+ func.params.forEach(function(param) {
+ if (param.intertype !== 'varargs') {
+ if (func.variables[param.ident]) warn('cannot have duplicate variable names: ' + param.ident); // toNiceIdent collisions?
+ func.variables[param.ident] = {
+ ident: param.ident,
+ type: param.type,
+ origin: 'funcparam',
+ lineNum: func.lineNum,
+ rawLinesIndex: -1
+ };
+ }
});
- // Function locals
-
- item.functions.forEach(function(func) {
- func.variables = {};
-
- // LLVM is SSA, so we always have a single assignment/write. We care about
- // the reads/other uses.
-
- // Function parameters
- func.params.forEach(function(param) {
- if (param.intertype !== 'varargs') {
- if (func.variables[param.ident]) warn('cannot have duplicate variable names: ' + param.ident); // toNiceIdent collisions?
- func.variables[param.ident] = {
- ident: param.ident,
- type: param.type,
- origin: 'funcparam',
- lineNum: func.lineNum,
- rawLinesIndex: -1
- };
+ // Normal variables
+ func.lines.forEach(function(item, i) {
+ if (item.assignTo) {
+ if (func.variables[item.assignTo]) warn('cannot have duplicate variable names: ' + item.assignTo); // toNiceIdent collisions?
+ var variable = func.variables[item.assignTo] = {
+ ident: item.assignTo,
+ type: item.type,
+ origin: item.intertype,
+ lineNum: item.lineNum,
+ rawLinesIndex: i
+ };
+ if (variable.origin === 'alloca') {
+ variable.allocatedNum = item.allocatedNum;
}
- });
-
- // Normal variables
- func.lines.forEach(function(item, i) {
- if (item.assignTo) {
- if (func.variables[item.assignTo]) warn('cannot have duplicate variable names: ' + item.assignTo); // toNiceIdent collisions?
- var variable = func.variables[item.assignTo] = {
- ident: item.assignTo,
- type: item.type,
- origin: item.intertype,
- lineNum: item.lineNum,
- rawLinesIndex: i
- };
- if (variable.origin === 'alloca') {
- variable.allocatedNum = item.allocatedNum;
- }
- if (variable.origin === 'call') {
- variable.type = getReturnType(variable.type);
- }
+ if (variable.origin === 'call') {
+ variable.type = getReturnType(variable.type);
}
- });
+ }
+ });
- if (QUANTUM_SIZE === 1) {
- // Second pass over variables - notice when types are crossed by bitcast
-
- func.lines.forEach(function(item) {
- if (item.assignTo && item.intertype === 'bitcast') {
- // bitcasts are unique in that they convert one pointer to another. We
- // sometimes need to know the original type of a pointer, so we save that.
- //
- // originalType is the type this variable is created from
- // derivedTypes are the types that this variable is cast into
- func.variables[item.assignTo].originalType = item.type2;
-
- if (!isNumber(item.assignTo)) {
- if (!func.variables[item.assignTo].derivedTypes) {
- func.variables[item.assignTo].derivedTypes = [];
- }
- func.variables[item.assignTo].derivedTypes.push(item.type);
+ if (QUANTUM_SIZE === 1) {
+ // Second pass over variables - notice when types are crossed by bitcast
+
+ func.lines.forEach(function(item) {
+ if (item.assignTo && item.intertype === 'bitcast') {
+ // bitcasts are unique in that they convert one pointer to another. We
+ // sometimes need to know the original type of a pointer, so we save that.
+ //
+ // originalType is the type this variable is created from
+ // derivedTypes are the types that this variable is cast into
+ func.variables[item.assignTo].originalType = item.type2;
+
+ if (!isNumber(item.assignTo)) {
+ if (!func.variables[item.assignTo].derivedTypes) {
+ func.variables[item.assignTo].derivedTypes = [];
}
+ func.variables[item.assignTo].derivedTypes.push(item.type);
}
- });
- }
+ }
+ });
+ }
- // Analyze variable uses
+ // Analyze variable uses
- function analyzeVariableUses() {
- dprint('vars', 'Analyzing variables for ' + func.ident + '\n');
+ function analyzeVariableUses() {
+ dprint('vars', 'Analyzing variables for ' + func.ident + '\n');
- for (vname in func.variables) {
- var variable = func.variables[vname];
+ for (vname in func.variables) {
+ var variable = func.variables[vname];
- // Whether the value itself is used. For an int, always yes. For a pointer,
- // we might never use the pointer's value - we might always just store to it /
- // read from it. If so, then we can optimize away the pointer.
- variable.hasValueTaken = false;
+ // Whether the value itself is used. For an int, always yes. For a pointer,
+ // we might never use the pointer's value - we might always just store to it /
+ // read from it. If so, then we can optimize away the pointer.
+ variable.hasValueTaken = false;
- variable.pointingLevels = pointingLevels(variable.type);
+ variable.pointingLevels = pointingLevels(variable.type);
- variable.uses = 0;
- }
+ variable.uses = 0;
+ }
- // TODO: improve the analysis precision. bitcast, for example, means we take the value, but perhaps we only use it to load/store
- var inNoop = 0;
- func.lines.forEach(function(line) {
- walkInterdata(line, function(item) {
- if (item.intertype == 'noop') inNoop++;
- if (!inNoop) {
- if (item.ident in func.variables) {
- func.variables[item.ident].uses++;
-
- if (item.intertype != 'load' && item.intertype != 'store') {
- func.variables[item.ident].hasValueTaken = true;
- }
+ // TODO: improve the analysis precision. bitcast, for example, means we take the value, but perhaps we only use it to load/store
+ var inNoop = 0;
+ func.lines.forEach(function(line) {
+ walkInterdata(line, function(item) {
+ if (item.intertype == 'noop') inNoop++;
+ if (!inNoop) {
+ if (item.ident in func.variables) {
+ func.variables[item.ident].uses++;
+
+ if (item.intertype != 'load' && item.intertype != 'store') {
+ func.variables[item.ident].hasValueTaken = true;
}
}
- }, function(item) {
- if (item.intertype == 'noop') inNoop--;
- });
+ }
+ }, function(item) {
+ if (item.intertype == 'noop') inNoop--;
});
+ });
- //if (dcheck('vars')) dprint('analyzed variables: ' + dump(func.variables));
- }
-
- analyzeVariableUses();
-
- // Decision time
+ //if (dcheck('vars')) dprint('analyzed variables: ' + dump(func.variables));
+ }
- for (vname in func.variables) {
- var variable = func.variables[vname];
- var pointedType = pointingLevels(variable.type) > 0 ? removePointing(variable.type) : null;
- if (variable.origin == 'getelementptr') {
- // Use our implementation that emulates pointers etc.
- // TODO Can we perhaps nativize some of these? However to do so, we need to discover their
- // true types; we have '?' for them now, as they cannot be discovered in the intertyper.
- variable.impl = VAR_EMULATED;
- } else if (variable.origin == 'funcparam') {
- variable.impl = VAR_EMULATED;
- } 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
- variable.impl = VAR_NATIVE;
- } else if (MICRO_OPTS && variable.origin === 'alloca' && !variable.hasValueTaken &&
- variable.allocatedNum === 1 &&
- (Runtime.isNumberType(pointedType) || Runtime.isPointerType(pointedType))) {
- // A pointer to a value which is only accessible through this pointer. Basically
- // a local value on the stack, which nothing fancy is done on. So we can
- // optimize away the pointing altogether, and just have a native variable
- variable.impl = VAR_NATIVIZED;
- } else {
- variable.impl = VAR_EMULATED;
- }
- if (dcheck('vars')) dprint('// var ' + vname + ': ' + JSON.stringify(variable));
+ analyzeVariableUses();
+
+ // Decision time
+
+ for (vname in func.variables) {
+ var variable = func.variables[vname];
+ var pointedType = pointingLevels(variable.type) > 0 ? removePointing(variable.type) : null;
+ if (variable.origin == 'getelementptr') {
+ // Use our implementation that emulates pointers etc.
+ // TODO Can we perhaps nativize some of these? However to do so, we need to discover their
+ // true types; we have '?' for them now, as they cannot be discovered in the intertyper.
+ variable.impl = VAR_EMULATED;
+ } else if (variable.origin == 'funcparam') {
+ variable.impl = VAR_EMULATED;
+ } 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
+ variable.impl = VAR_NATIVE;
+ } else if (MICRO_OPTS && variable.origin === 'alloca' && !variable.hasValueTaken &&
+ variable.allocatedNum === 1 &&
+ (Runtime.isNumberType(pointedType) || Runtime.isPointerType(pointedType))) {
+ // A pointer to a value which is only accessible through this pointer. Basically
+ // a local value on the stack, which nothing fancy is done on. So we can
+ // optimize away the pointing altogether, and just have a native variable
+ variable.impl = VAR_NATIVIZED;
+ } else {
+ variable.impl = VAR_EMULATED;
}
- });
- this.forwardItem(item, 'Signalyzer');
- }
- });
+ if (dcheck('vars')) dprint('// var ' + vname + ': ' + JSON.stringify(variable));
+ }
+ });
+ }
// Sign analyzer
//
@@ -1251,214 +1231,208 @@ function analyzer(data, sidePass) {
// to see where it is used. We only care about mathops, since only they
// need signs.
//
- substrate.addActor('Signalyzer', {
- processItem: function(item) {
- this.forwardItem(item, 'QuantumFixer');
- if (USE_TYPED_ARRAYS != 2 || CORRECT_SIGNS == 1) return;
-
- function seekIdent(item, obj) {
- if (item.ident === obj.ident) {
- obj.found++;
- }
+ function signalyzer() {
+ if (USE_TYPED_ARRAYS != 2 || CORRECT_SIGNS == 1) return;
+
+ function seekIdent(item, obj) {
+ if (item.ident === obj.ident) {
+ obj.found++;
}
+ }
- function seekMathop(item, obj) {
- if (item.intertype === 'mathop' && obj.found && !obj.decided) {
- if (isUnsignedOp(item.op, item.variant)) {
- obj.unsigned++;
- } else {
- obj.signed++;
- }
+ function seekMathop(item, obj) {
+ if (item.intertype === 'mathop' && obj.found && !obj.decided) {
+ if (isUnsignedOp(item.op, item.variant)) {
+ obj.unsigned++;
+ } else {
+ obj.signed++;
}
}
+ }
- item.functions.forEach(function(func) {
- func.lines.forEach(function(line, i) {
- if (line.intertype === 'load') {
- // Floats have no concept of signedness. Mark them as 'signed', which is the default, for which we do nothing
- if (line.type in Runtime.FLOAT_TYPES) {
- line.unsigned = false;
- return;
- }
- // Booleans are always unsigned
- var data = func.variables[line.assignTo];
- if (data.type === 'i1') {
- line.unsigned = true;
- return;
- }
-
- var total = data.uses;
- if (total === 0) return;
- var obj = { ident: line.assignTo, found: 0, unsigned: 0, signed: 0, total: total };
- // in loops with phis, we can also be used *before* we are defined
- var j = i-1, k = i+1;
- while(1) {
- assert(j >= 0 || k < func.lines.length, 'Signalyzer ran out of space to look for sign indications for line ' + line.lineNum);
- if (j >= 0 && walkInterdata(func.lines[j], seekIdent, seekMathop, obj)) break;
- if (k < func.lines.length && walkInterdata(func.lines[k], seekIdent, seekMathop, obj)) break;
- if (obj.total && obj.found >= obj.total) break; // see comment below
- j -= 1;
- k += 1;
- }
+ item.functions.forEach(function(func) {
+ func.lines.forEach(function(line, i) {
+ if (line.intertype === 'load') {
+ // Floats have no concept of signedness. Mark them as 'signed', which is the default, for which we do nothing
+ if (line.type in Runtime.FLOAT_TYPES) {
+ line.unsigned = false;
+ return;
+ }
+ // Booleans are always unsigned
+ var data = func.variables[line.assignTo];
+ if (data.type === 'i1') {
+ line.unsigned = true;
+ return;
+ }
- // unsigned+signed might be < total, since the same ident can appear multiple times in the same mathop.
- // found can actually be > total, since we currently have the same ident in a GEP (see cubescript test)
- // in the GEP item, and a child item (we have the ident copied onto the GEP item as a convenience).
- // probably not a bug-causer, but FIXME. see also a reference to this above
- // we also leave the loop above potentially early due to this. otherwise, though, we end up scanning the
- // entire function in some cases which is very slow
- assert(obj.found >= obj.total, 'Could not Signalyze line ' + line.lineNum);
- line.unsigned = obj.unsigned > 0;
- dprint('vars', 'Signalyzer: ' + line.assignTo + ' has unsigned == ' + line.unsigned + ' (line ' + line.lineNum + ')');
+ var total = data.uses;
+ if (total === 0) return;
+ var obj = { ident: line.assignTo, found: 0, unsigned: 0, signed: 0, total: total };
+ // in loops with phis, we can also be used *before* we are defined
+ var j = i-1, k = i+1;
+ while(1) {
+ assert(j >= 0 || k < func.lines.length, 'Signalyzer ran out of space to look for sign indications for line ' + line.lineNum);
+ if (j >= 0 && walkInterdata(func.lines[j], seekIdent, seekMathop, obj)) break;
+ if (k < func.lines.length && walkInterdata(func.lines[k], seekIdent, seekMathop, obj)) break;
+ if (obj.total && obj.found >= obj.total) break; // see comment below
+ j -= 1;
+ k += 1;
}
- });
+
+ // unsigned+signed might be < total, since the same ident can appear multiple times in the same mathop.
+ // found can actually be > total, since we currently have the same ident in a GEP (see cubescript test)
+ // in the GEP item, and a child item (we have the ident copied onto the GEP item as a convenience).
+ // probably not a bug-causer, but FIXME. see also a reference to this above
+ // we also leave the loop above potentially early due to this. otherwise, though, we end up scanning the
+ // entire function in some cases which is very slow
+ assert(obj.found >= obj.total, 'Could not Signalyze line ' + line.lineNum);
+ line.unsigned = obj.unsigned > 0;
+ dprint('vars', 'Signalyzer: ' + line.assignTo + ' has unsigned == ' + line.unsigned + ' (line ' + line.lineNum + ')');
+ }
});
- }
- });
+ });
+ }
// Quantum fixer
//
// See settings.js for the meaning of QUANTUM_SIZE. The issue we fix here is,
// to correct the .ll assembly code so that things work with QUANTUM_SIZE=1.
//
- substrate.addActor('QuantumFixer', {
- processItem: function(item) {
- this.forwardItem(item, 'LabelAnalyzer');
- if (QUANTUM_SIZE !== 1) return;
-
- // ptrs: the indexes of parameters that are pointers, whose originalType is what we want
- // bytes: the index of the 'bytes' parameter
- // TODO: malloc, realloc?
- var FIXABLE_CALLS = {
- 'memcpy': { ptrs: [0,1], bytes: 2 },
- 'memmove': { ptrs: [0,1], bytes: 2 },
- 'memset': { ptrs: [0], bytes: 2 },
- 'qsort': { ptrs: [0], bytes: 2 }
- };
+ function quantumFixer() {
+ if (QUANTUM_SIZE !== 1) return;
+
+ // ptrs: the indexes of parameters that are pointers, whose originalType is what we want
+ // bytes: the index of the 'bytes' parameter
+ // TODO: malloc, realloc?
+ var FIXABLE_CALLS = {
+ 'memcpy': { ptrs: [0,1], bytes: 2 },
+ 'memmove': { ptrs: [0,1], bytes: 2 },
+ 'memset': { ptrs: [0], bytes: 2 },
+ 'qsort': { ptrs: [0], bytes: 2 }
+ };
- function getSize(types, type, fat) {
- if (types[type]) return types[type].flatSize;
- if (fat) {
- Runtime.QUANTUM_SIZE = 4;
- }
- var ret = Runtime.getNativeTypeSize(type);
- if (fat) {
- Runtime.QUANTUM_SIZE = 1;
- }
- return ret;
+ function getSize(types, type, fat) {
+ if (types[type]) return types[type].flatSize;
+ if (fat) {
+ Runtime.QUANTUM_SIZE = 4;
}
-
- function getFlatIndexes(types, type) {
- if (types[type]) return types[type].flatIndexes;
- return [0];
+ var ret = Runtime.getNativeTypeSize(type);
+ if (fat) {
+ Runtime.QUANTUM_SIZE = 1;
}
+ return ret;
+ }
- item.functions.forEach(function(func) {
- function getOriginalType(param) {
- function get() {
- if (param.intertype === 'value' && !isNumber(param.ident)) {
- if (func.variables[param.ident]) {
- return func.variables[param.ident].originalType || null;
- } else {
- return item.globalVariables[param.ident].originalType;
- }
- } else if (param.intertype === 'bitcast') {
- return param.params[0].type;
- } else if (param.intertype === 'getelementptr') {
- if (param.params[0].type[0] === '[') return param.params[0].type;
- }
- return null;
- }
- var ret = get();
- if (ret && ret[0] === '[') {
- var check = /^\[(\d+)\ x\ (.*)\]\*$/.exec(ret);
- assert(check);
- ret = check[2] + '*';
- }
- return ret;
- }
+ function getFlatIndexes(types, type) {
+ if (types[type]) return types[type].flatIndexes;
+ return [0];
+ }
- func.lines.forEach(function(line) {
- // Call
- if (line.intertype === 'call') {
- var funcIdent = LibraryManager.getRootIdent(line.ident.substr(1));
- var fixData = FIXABLE_CALLS[funcIdent];
- if (!fixData) return;
- var ptrs = fixData.ptrs.map(function(ptr) { return line.params[ptr] });
- var bytes = line.params[fixData.bytes].ident;
-
- // Only consider original types. This assumes memcpy always has pointers bitcast to i8*
- var originalTypes = ptrs.map(getOriginalType);
- for (var i = 0; i < originalTypes.length; i++) {
- if (!originalTypes[i]) return;
- }
- originalTypes = originalTypes.map(function(type) { return removePointing(type) });
- var sizes = originalTypes.map(function(type) { return getSize(Types.types, type) });
- var fatSizes = originalTypes.map(function(type) { return getSize(Types.fatTypes, type, true) });
- // The sizes may not be identical, if we copy a descendant class into a parent class. We use
- // the smaller size in that case. However, this may also be a bug, it is hard to tell, hence a warning
- warn(dedup(sizes).length === 1, 'All sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' +
- line.lineNum);
- warn(dedup(fatSizes).length === 1, 'All fat sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' +
- line.lineNum);
- var size = Math.min.apply(null, sizes);
- var fatSize = Math.min.apply(null, fatSizes);
- if (isNumber(bytes)) {
- // Figure out how much to copy.
- var fixedBytes;
- if (bytes % fatSize === 0) {
- fixedBytes = size*(bytes/fatSize);
- } else if (fatSize % bytes === 0 && size % (fatSize/bytes) === 0) {
- // Assume this is a simple array. XXX We can be wrong though! See next TODO
- fixedBytes = size/(fatSize/bytes);
- } else {
- // Just part of a structure. Align them to see how many fields. Err on copying more.
- // TODO: properly generate a complete structure, including nesteds, and calculate on that
- var flatIndexes = getFlatIndexes(Types.types, originalTypes[0]).concat(size);
- var fatFlatIndexes = getFlatIndexes(Types.fatTypes, originalTypes[0]).concat(fatSize);
- var index = 0;
- var left = bytes;
- fixedBytes = 0;
- while (left > 0) {
- left -= fatFlatIndexes[index+1] - fatFlatIndexes[index]; // note: we copy the alignment bytes too, which is unneeded
- fixedBytes += flatIndexes[index+1] - flatIndexes[index];
- }
- }
- line.params[fixData.bytes].ident = fixedBytes;
+ item.functions.forEach(function(func) {
+ function getOriginalType(param) {
+ function get() {
+ if (param.intertype === 'value' && !isNumber(param.ident)) {
+ if (func.variables[param.ident]) {
+ return func.variables[param.ident].originalType || null;
} else {
- line.params[fixData.bytes].intertype = 'jsvalue';
- // We have an assertion in library::memcpy() that this is round
- line.params[fixData.bytes].ident = size + '*(' + bytes + '/' + fatSize + ')';
+ return item.globalVariables[param.ident].originalType;
}
+ } else if (param.intertype === 'bitcast') {
+ return param.params[0].type;
+ } else if (param.intertype === 'getelementptr') {
+ if (param.params[0].type[0] === '[') return param.params[0].type;
}
- });
- });
+ return null;
+ }
+ var ret = get();
+ if (ret && ret[0] === '[') {
+ var check = /^\[(\d+)\ x\ (.*)\]\*$/.exec(ret);
+ assert(check);
+ ret = check[2] + '*';
+ }
+ return ret;
+ }
- // 2nd part - fix hardcoded constant offsets in global constants
- values(item.globalVariables).forEach(function(variable) {
- function recurse(item) {
- if (item.contents) {
- item.contents.forEach(recurse);
- } else if (item.intertype === 'getelementptr' && item.params[0].intertype === 'bitcast' && item.params[0].type === 'i8*') {
- var originalType = removePointing(item.params[0].params[0].type);
- var fatSize = getSize(Types.fatTypes, originalType, true);
- var slimSize = getSize(Types.types, originalType, false);
- assert(fatSize % slimSize === 0);
- item.params.slice(1).forEach(function(param) {
- if (param.intertype === 'value' && isNumber(param.ident)) {
- var corrected = parseInt(param.ident)/(fatSize/slimSize);
- assert(corrected % 1 === 0);
- param.ident = corrected.toString();
+ func.lines.forEach(function(line) {
+ // Call
+ if (line.intertype === 'call') {
+ var funcIdent = LibraryManager.getRootIdent(line.ident.substr(1));
+ var fixData = FIXABLE_CALLS[funcIdent];
+ if (!fixData) return;
+ var ptrs = fixData.ptrs.map(function(ptr) { return line.params[ptr] });
+ var bytes = line.params[fixData.bytes].ident;
+
+ // Only consider original types. This assumes memcpy always has pointers bitcast to i8*
+ var originalTypes = ptrs.map(getOriginalType);
+ for (var i = 0; i < originalTypes.length; i++) {
+ if (!originalTypes[i]) return;
+ }
+ originalTypes = originalTypes.map(function(type) { return removePointing(type) });
+ var sizes = originalTypes.map(function(type) { return getSize(Types.types, type) });
+ var fatSizes = originalTypes.map(function(type) { return getSize(Types.fatTypes, type, true) });
+ // The sizes may not be identical, if we copy a descendant class into a parent class. We use
+ // the smaller size in that case. However, this may also be a bug, it is hard to tell, hence a warning
+ warn(dedup(sizes).length === 1, 'All sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' +
+ line.lineNum);
+ warn(dedup(fatSizes).length === 1, 'All fat sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' +
+ line.lineNum);
+ var size = Math.min.apply(null, sizes);
+ var fatSize = Math.min.apply(null, fatSizes);
+ if (isNumber(bytes)) {
+ // Figure out how much to copy.
+ var fixedBytes;
+ if (bytes % fatSize === 0) {
+ fixedBytes = size*(bytes/fatSize);
+ } else if (fatSize % bytes === 0 && size % (fatSize/bytes) === 0) {
+ // Assume this is a simple array. XXX We can be wrong though! See next TODO
+ fixedBytes = size/(fatSize/bytes);
+ } else {
+ // Just part of a structure. Align them to see how many fields. Err on copying more.
+ // TODO: properly generate a complete structure, including nesteds, and calculate on that
+ var flatIndexes = getFlatIndexes(Types.types, originalTypes[0]).concat(size);
+ var fatFlatIndexes = getFlatIndexes(Types.fatTypes, originalTypes[0]).concat(fatSize);
+ var index = 0;
+ var left = bytes;
+ fixedBytes = 0;
+ while (left > 0) {
+ left -= fatFlatIndexes[index+1] - fatFlatIndexes[index]; // note: we copy the alignment bytes too, which is unneeded
+ fixedBytes += flatIndexes[index+1] - flatIndexes[index];
}
- });
- } else if (item.params) {
- item.params.forEach(recurse);
+ }
+ line.params[fixData.bytes].ident = fixedBytes;
+ } else {
+ line.params[fixData.bytes].intertype = 'jsvalue';
+ // We have an assertion in library::memcpy() that this is round
+ line.params[fixData.bytes].ident = size + '*(' + bytes + '/' + fatSize + ')';
}
}
- if (!variable.external && variable.value) recurse(variable.value);
});
- }
- });
+ });
+
+ // 2nd part - fix hardcoded constant offsets in global constants
+ values(item.globalVariables).forEach(function(variable) {
+ function recurse(item) {
+ if (item.contents) {
+ item.contents.forEach(recurse);
+ } else if (item.intertype === 'getelementptr' && item.params[0].intertype === 'bitcast' && item.params[0].type === 'i8*') {
+ var originalType = removePointing(item.params[0].params[0].type);
+ var fatSize = getSize(Types.fatTypes, originalType, true);
+ var slimSize = getSize(Types.types, originalType, false);
+ assert(fatSize % slimSize === 0);
+ item.params.slice(1).forEach(function(param) {
+ if (param.intertype === 'value' && isNumber(param.ident)) {
+ var corrected = parseInt(param.ident)/(fatSize/slimSize);
+ assert(corrected % 1 === 0);
+ param.ident = corrected.toString();
+ }
+ });
+ } else if (item.params) {
+ item.params.forEach(recurse);
+ }
+ }
+ if (!variable.external && variable.value) recurse(variable.value);
+ });
+ }
function operateOnLabels(line, func) {
function process(item, id) {
@@ -1477,268 +1451,260 @@ function analyzer(data, sidePass) {
}
// Label analyzer
- substrate.addActor('LabelAnalyzer', {
- processItem: function(item) {
- item.functions.forEach(function(func) {
- func.labelsDict = {};
- func.labelIds = {};
- func.labelIdsInverse = {};
- func.labelIdCounter = 1;
- func.labels.forEach(function(label) {
- if (!(label.ident in func.labelIds)) {
- func.labelIds[label.ident] = func.labelIdCounter++;
- func.labelIdsInverse[func.labelIdCounter-1] = label.ident;
- }
- });
- var entryIdent = func.labels[0].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
- });
+ function labelAnalyzer() {
+ item.functions.forEach(function(func) {
+ func.labelsDict = {};
+ func.labelIds = {};
+ func.labelIdsInverse = {};
+ func.labelIdCounter = 1;
+ func.labels.forEach(function(label) {
+ if (!(label.ident in func.labelIds)) {
+ func.labelIds[label.ident] = func.labelIdCounter++;
+ func.labelIdsInverse[func.labelIdCounter-1] = label.ident;
+ }
+ });
+ var entryIdent = func.labels[0].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;
- });
+ 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];
- if (VERBOSE && !phi.params[i].label) warn('phi refers to nonexistent label on line ' + phi.lineNum);
- }
+ // 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];
+ if (VERBOSE && !phi.params[i].label) warn('phi refers to nonexistent label on line ' + phi.lineNum);
}
- });
- });
-
- func.lines.forEach(function(line) {
- if (line.intertype == 'indirectbr') {
- func.forceEmulated = true;
}
});
+ });
- function getActualLabelId(labelId) {
- if (func.labelsDict[labelId]) return labelId;
- // If not present, it must be a surprisingly-named entry (or undefined behavior, in which case, still ok to use the entry)
- labelId = func.labelIds[entryIdent];
- assert(func.labelsDict[labelId]);
- return labelId;
+ func.lines.forEach(function(line) {
+ if (line.intertype == 'indirectbr') {
+ func.forceEmulated = true;
}
+ });
- // Basic longjmp support, see library.js setjmp/longjmp
- var setjmp = toNiceIdent('@setjmp');
- func.setjmpTable = null;
- for (var i = 0; i < func.labels.length; i++) {
- var label = func.labels[i];
- for (var j = 0; j < label.lines.length; j++) {
- var line = label.lines[j];
- if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
- // Add a new label
- var oldLabel = label.ident;
- var newLabel = func.labelIdCounter++;
- if (!func.setjmpTable) func.setjmpTable = [];
- func.setjmpTable.push({ oldLabel: oldLabel, newLabel: newLabel, assignTo: line.assignTo });
- func.labels.splice(i+1, 0, {
- intertype: 'label',
- ident: newLabel,
- lineNum: label.lineNum + 0.5,
- lines: label.lines.slice(j+1)
- });
- func.labelsDict[newLabel] = func.labels[i+1];
- label.lines = label.lines.slice(0, j+1);
- label.lines.push({
- intertype: 'branch',
- label: toNiceIdent(newLabel),
- lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this
- });
- // 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++) {
- var sourceLabelId = getActualLabelId(phi.params[i].label);
- if (sourceLabelId == oldLabel) {
- phi.params[i].label = newLabel;
- }
+ function getActualLabelId(labelId) {
+ if (func.labelsDict[labelId]) return labelId;
+ // If not present, it must be a surprisingly-named entry (or undefined behavior, in which case, still ok to use the entry)
+ labelId = func.labelIds[entryIdent];
+ assert(func.labelsDict[labelId]);
+ return labelId;
+ }
+
+ // Basic longjmp support, see library.js setjmp/longjmp
+ var setjmp = toNiceIdent('@setjmp');
+ func.setjmpTable = null;
+ for (var i = 0; i < func.labels.length; i++) {
+ var label = func.labels[i];
+ for (var j = 0; j < label.lines.length; j++) {
+ var line = label.lines[j];
+ if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
+ // Add a new label
+ var oldLabel = label.ident;
+ var newLabel = func.labelIdCounter++;
+ if (!func.setjmpTable) func.setjmpTable = [];
+ func.setjmpTable.push({ oldLabel: oldLabel, newLabel: newLabel, assignTo: line.assignTo });
+ func.labels.splice(i+1, 0, {
+ intertype: 'label',
+ ident: newLabel,
+ lineNum: label.lineNum + 0.5,
+ lines: label.lines.slice(j+1)
+ });
+ func.labelsDict[newLabel] = func.labels[i+1];
+ label.lines = label.lines.slice(0, j+1);
+ label.lines.push({
+ intertype: 'branch',
+ label: toNiceIdent(newLabel),
+ lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this
+ });
+ // 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++) {
+ var sourceLabelId = getActualLabelId(phi.params[i].label);
+ if (sourceLabelId == oldLabel) {
+ phi.params[i].label = newLabel;
}
}
- });
+ }
});
- }
+ });
}
}
- if (func.setjmpTable) {
- func.forceEmulated = true;
- recomputeLines(func);
- }
-
- // Properly implement phis, by pushing them back into the branch
- // that leads to here. We will only have the |var| definition in this location.
+ }
+ if (func.setjmpTable) {
+ func.forceEmulated = true;
+ recomputeLines(func);
+ }
- // First, push phis back
- func.labels.forEach(function(label) {
- label.lines.forEach(function(phi) {
- if (phi.intertype == 'phi') {
- for (var i = 0; i < phi.params.length; i++) {
- var param = phi.params[i];
- if (VERBOSE && !param.label) warn('phi refers to nonexistent label on line ' + phi.lineNum);
- var sourceLabelId = getActualLabelId(param.label);
- if (sourceLabelId) {
- var sourceLabel = func.labelsDict[sourceLabelId];
- var lastLine = sourceLabel.lines.slice(-1)[0];
- assert(lastLine.intertype in LLVM.PHI_REACHERS, 'Only some can lead to labels with phis:' + [func.ident, label.ident, lastLine.intertype]);
- if (!lastLine.phi) {
- lastLine.phi = true;
- assert(!lastLine.dependent);
- lastLine.dependent = {
- intertype: 'phiassigns',
- params: []
- };
+ // Properly implement phis, by pushing them back into the branch
+ // that leads to here. We will only have the |var| definition in this location.
+
+ // First, push phis back
+ func.labels.forEach(function(label) {
+ label.lines.forEach(function(phi) {
+ if (phi.intertype == 'phi') {
+ for (var i = 0; i < phi.params.length; i++) {
+ var param = phi.params[i];
+ if (VERBOSE && !param.label) warn('phi refers to nonexistent label on line ' + phi.lineNum);
+ var sourceLabelId = getActualLabelId(param.label);
+ if (sourceLabelId) {
+ var sourceLabel = func.labelsDict[sourceLabelId];
+ var lastLine = sourceLabel.lines.slice(-1)[0];
+ assert(lastLine.intertype in LLVM.PHI_REACHERS, 'Only some can lead to labels with phis:' + [func.ident, label.ident, lastLine.intertype]);
+ if (!lastLine.phi) {
+ lastLine.phi = true;
+ assert(!lastLine.dependent);
+ lastLine.dependent = {
+ intertype: 'phiassigns',
+ params: []
};
- lastLine.dependent.params.push({
- intertype: 'phiassign',
- ident: phi.assignTo,
- value: param.value,
- targetLabel: label.ident
- });
- }
+ };
+ lastLine.dependent.params.push({
+ intertype: 'phiassign',
+ ident: phi.assignTo,
+ value: param.value,
+ targetLabel: label.ident
+ });
}
- // The assign to phi is now just a var
- phi.intertype = 'var';
- phi.ident = phi.assignTo;
- phi.assignTo = null;
}
- });
+ // The assign to phi is now just a var
+ phi.intertype = 'var';
+ phi.ident = phi.assignTo;
+ phi.assignTo = null;
+ }
});
+ });
- if (func.ident in NECESSARY_BLOCKADDRS) {
- Functions.blockAddresses[func.ident] = {};
- for (var needed in NECESSARY_BLOCKADDRS[func.ident]) {
- assert(needed in func.labelIds);
- Functions.blockAddresses[func.ident][needed] = func.labelIds[needed];
- }
+ if (func.ident in NECESSARY_BLOCKADDRS) {
+ Functions.blockAddresses[func.ident] = {};
+ for (var needed in NECESSARY_BLOCKADDRS[func.ident]) {
+ assert(needed in func.labelIds);
+ Functions.blockAddresses[func.ident][needed] = func.labelIds[needed];
}
- });
- this.forwardItem(item, 'StackAnalyzer');
- }
- });
+ }
+ });
+ }
// Stack analyzer - calculate the base stack usage
- substrate.addActor('StackAnalyzer', {
- processItem: function(data) {
- data.functions.forEach(function(func) {
- var lines = func.labels[0].lines;
- for (var i = 0; i < lines.length; i++) {
- var item = lines[i];
- if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
- item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ?
- calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0;
- if (USE_TYPED_ARRAYS === 2) {
- // We need to keep the stack aligned
- item.allocatedSize = Runtime.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN);
- }
+ function stackAnalyzer() {
+ data.functions.forEach(function(func) {
+ var lines = func.labels[0].lines;
+ for (var i = 0; i < lines.length; i++) {
+ var item = lines[i];
+ if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
+ item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ?
+ calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0;
+ if (USE_TYPED_ARRAYS === 2) {
+ // We need to keep the stack aligned
+ item.allocatedSize = Runtime.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN);
}
- var index = 0;
- for (var i = 0; i < lines.length; i++) {
- var item = lines[i];
- if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
- item.allocatedIndex = index;
- index += item.allocatedSize;
- delete item.allocatedSize;
- }
- func.initialStack = index;
- func.otherStackAllocations = false;
- while (func.initialStack == 0) { // one-time loop with possible abort in the middle
- // If there is no obvious need for stack management, perhaps we don't need it
- // (we try to optimize that way with SKIP_STACK_IN_SMALL). However,
- // we need to note if stack allocations other than initial allocs can happen here
- // If so, we need to rewind the stack when we leave.
-
- // By-value params are causes of additional allocas (although we could in theory make them normal allocas too)
- func.params.forEach(function(param) {
- if (param.byVal) {
- func.otherStackAllocations = true;
- }
- });
- if (func.otherStackAllocations) break;
+ }
+ var index = 0;
+ for (var i = 0; i < lines.length; i++) {
+ var item = lines[i];
+ if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
+ item.allocatedIndex = index;
+ index += item.allocatedSize;
+ delete item.allocatedSize;
+ }
+ func.initialStack = index;
+ func.otherStackAllocations = false;
+ while (func.initialStack == 0) { // one-time loop with possible abort in the middle
+ // If there is no obvious need for stack management, perhaps we don't need it
+ // (we try to optimize that way with SKIP_STACK_IN_SMALL). However,
+ // we need to note if stack allocations other than initial allocs can happen here
+ // If so, we need to rewind the stack when we leave.
+
+ // By-value params are causes of additional allocas (although we could in theory make them normal allocas too)
+ func.params.forEach(function(param) {
+ if (param.byVal) {
+ func.otherStackAllocations = true;
+ }
+ });
+ if (func.otherStackAllocations) break;
- // Allocas
- var finishedInitial = false;
+ // Allocas
+ var finishedInitial = false;
- lines = func.lines; // We need to consider all the function lines now, not just the first label
+ lines = func.lines; // We need to consider all the function lines now, not just the first label
- for (var i = 0; i < lines.length; i++) {
- var item = lines[i];
- if (!finishedInitial && (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum))) {
- finishedInitial = true;
- }
- if (item.intertype == 'alloca' && finishedInitial) {
- func.otherStackAllocations = true;
- break;
- }
+ for (var i = 0; i < lines.length; i++) {
+ var item = lines[i];
+ if (!finishedInitial && (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum))) {
+ finishedInitial = true;
}
- if (func.otherStackAllocations) break;
-
- // Varargs
- for (var i = 0; i < lines.length; i++) {
- var item = lines[i];
- if (item.intertype == 'call' && isVarArgsFunctionType(item.type)) {
- func.otherStackAllocations = true;
- break;
- }
+ if (item.intertype == 'alloca' && finishedInitial) {
+ func.otherStackAllocations = true;
+ break;
}
- if (func.otherStackAllocations) break;
+ }
+ if (func.otherStackAllocations) break;
- break;
+ // Varargs
+ for (var i = 0; i < lines.length; i++) {
+ var item = lines[i];
+ if (item.intertype == 'call' && isVarArgsFunctionType(item.type)) {
+ func.otherStackAllocations = true;
+ break;
+ }
}
- });
- this.forwardItem(data, 'Relooper');
- }
- });
+ if (func.otherStackAllocations) break;
- // ReLooper - reconstruct nice loops, as much as possible
- // This is now done in the jsify stage, using compiled relooper2
- substrate.addActor('Relooper', {
- processItem: function(item) {
- function finish() {
- item.__finalResult__ = true;
- return [item];
- }
- function makeBlock(labels, entries, labelsDict, forceEmulated) {
- if (labels.length == 0) return null;
- dprint('relooping', 'prelooping: ' + entries + ',' + labels.length + ' labels');
- assert(entries && entries[0]); // need at least 1 entry
-
- var emulated = {
- type: 'emulated',
- id: 'B',
- labels: labels,
- entries: entries.slice(0)
- };
- return emulated;
+ break;
}
- item.functions.forEach(function(func) {
- dprint('relooping', "// relooping function: " + func.ident);
- func.block = makeBlock(func.labels, [func.labels[0].ident], func.labelsDict, func.forceEmulated);
- });
+ });
+ }
- return finish();
+ // ReLooper - reconstruct nice loops, as much as possible
+ // This is now done in the jsify stage, using compiled relooper2
+ function relooper() {
+ function makeBlock(labels, entries, labelsDict, forceEmulated) {
+ if (labels.length == 0) return null;
+ dprint('relooping', 'prelooping: ' + entries + ',' + labels.length + ' labels');
+ assert(entries && entries[0]); // need at least 1 entry
+
+ var emulated = {
+ type: 'emulated',
+ id: 'B',
+ labels: labels,
+ entries: entries.slice(0)
+ };
+ return emulated;
}
- });
-
- // Data
- substrate.addItem({
- items: data
- }, 'Sorter');
+ item.functions.forEach(function(func) {
+ dprint('relooping', "// relooping function: " + func.ident);
+ func.block = makeBlock(func.labels, [func.labels[0].ident], func.labelsDict, func.forceEmulated);
+ });
+ }
- // Solve it
- return substrate.solve();
+ // main
+ castAway();
+ legalizer();
+ typevestigator();
+ analyzeTypes();
+ variableAnalyzer();
+ signalyzer();
+ quantumFixer();
+ labelAnalyzer();
+ stackAnalyzer();
+ relooper();
+
+ return item;
}