aboutsummaryrefslogtreecommitdiff
path: root/src/analyzer.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/analyzer.js')
-rw-r--r--src/analyzer.js78
1 files changed, 51 insertions, 27 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 3f9025da..a131406c 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -20,6 +20,7 @@ var BRANCH_INVOKE = set('branch', 'invoke');
var LABEL_ENDERS = set('branch', 'return', 'switch');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
+var I64_DOUBLE_FLIP = { i64: 'double', double: 'i64' };
// Analyzer
@@ -103,61 +104,81 @@ function analyzer(data, sidePass) {
}
});
- // CastAway - try to remove bitcasts of double to i64, which LLVM sometimes generates unnecessarily
+ // CastAway - try to remove bitcasts of double<-->i64, which LLVM sometimes generates unnecessarily
// (load a double, convert to i64, use as i64).
- // We optimize this by checking if the value is later converted to an i64. If so we create a shadow
- // variable that is a load of an i64, and use that in those places. (As SSA, this is valid, and
+ // We optimize this by checking if there are such bitcasts. If so we create a shadow
+ // variable that is of the other type, and use that in the relevant places. (As SSA, this is valid, and
// variable elimination later will remove the double load if it is no longer needed.)
//
+ // 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;
item.functions.forEach(function(func) {
- var changed = false;
+ 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 I64_DOUBLE_FLIP) {
+ has = true;
+ }
+ }
+ });
+ if (!has) return;
+ // there are i64<-->double bitcasts, create shadows for everything
+ var shadowed = {};
func.labels.forEach(function(label) {
- var doubleVars = {};
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 == 'double') {
- doubleVars[line.assignTo] = i;
- } else if (line.intertype == 'bitcast' && line.type == 'i64' && line.ident in doubleVars) {
- // this is a bitcast of a loaded double into an i64. create shadow var
- var shadow = line.ident + '$$i64doubleSHADOW';
- var loadI = doubleVars[line.ident];
- var load = lines[loadI];
- if (load.pointer.intertype != 'value') { i++; continue } // TODO
- // create shadow
- lines.splice(loadI + 1, 0, { // this element will be legalized in the next phase
+ if (line.intertype == 'load' && line.type in I64_DOUBLE_FLIP) {
+ if (line.pointer.intertype != 'value') { i++; continue } // TODO
+ shadowed[line.assignTo] = 1;
+ var shadow = line.assignTo + '$$SHADOW';
+ var flip = I64_DOUBLE_FLIP[line.type];
+ lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase
tokens: null,
indent: 2,
- lineNum: load.lineNum + 0.5,
+ lineNum: line.lineNum + 0.5,
assignTo: shadow,
intertype: 'load',
- pointerType: 'i64*',
- type: 'i64',
- valueType: 'i64',
+ pointerType: flip + '*',
+ type: flip,
+ valueType: flip,
pointer: {
intertype: 'value',
- ident: load.pointer.ident,
- type: 'i64*'
+ ident: line.pointer.ident,
+ type: flip + '*'
},
- align: load.align,
- ident: load.ident
+ align: line.align,
+ ident: line.ident
});
- // use shadow
- line.params[0].ident = shadow;
- line.params[0].type = 'i64';
- line.type2 = 'i64';
// note: no need to update func.lines, it is generated in a later pass
i++;
}
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 I64_DOUBLE_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;
+ }
+ }
+ });
});
}
});
@@ -233,12 +254,15 @@ function analyzer(data, sidePass) {
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) {