aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js2766
-rw-r--r--src/compiler.js15
-rw-r--r--src/compiler_phase.html33
-rw-r--r--src/framework.js257
-rw-r--r--src/intertyper.js1850
-rw-r--r--src/jsifier.js1222
-rw-r--r--src/library.js158
-rw-r--r--src/library_fs.js72
-rw-r--r--src/library_gl.js168
-rw-r--r--src/library_idbfs.js216
-rw-r--r--src/library_memfs.js26
-rw-r--r--src/library_nodefs.js234
-rw-r--r--src/library_sdl.js280
-rw-r--r--src/library_sockfs.js12
-rw-r--r--src/modules.js6
-rw-r--r--src/parseTools.js211
-rw-r--r--src/postamble.js3
-rw-r--r--src/preamble.js25
-rw-r--r--src/relooper/Relooper.cpp2
-rw-r--r--src/relooper/emscripten/glue.js11
-rw-r--r--src/runtime.js41
-rw-r--r--src/settings.js3
-rw-r--r--src/utility.js11
23 files changed, 4181 insertions, 3441 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;
}
diff --git a/src/compiler.js b/src/compiler.js
index f7c6dd59..90060837 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -121,6 +121,8 @@ if (typeof print === 'undefined') {
// *** Environment setup code ***
+DEBUG_MEMORY = false;
+
// Basic utilities
load('utility.js');
@@ -211,15 +213,19 @@ C_DEFINES = temp.defines;
// Load compiler code
-load('framework.js');
load('modules.js');
load('parseTools.js');
load('intertyper.js');
load('analyzer.js');
load('jsifier.js');
-if (RELOOP) {
+if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase
RelooperModule = { TOTAL_MEMORY: ceilPowerOfTwo(2*RELOOPER_BUFFER_SIZE) };
- load(RELOOPER);
+ try {
+ load(RELOOPER);
+ } catch(e) {
+ printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir');
+ load('relooper.js');
+ }
assert(typeof Relooper != 'undefined');
}
globalEval(processMacros(preprocess(read('runtime.js'))));
@@ -273,6 +279,9 @@ function compile(raw) {
intertyped = null;
JSify(analyzed);
+ //dumpInterProf();
+ //printErr(phase + ' paths (fast, slow): ' + [fastPaths, slowPaths]);
+
phase = null;
if (DEBUG_MEMORY) {
diff --git a/src/compiler_phase.html b/src/compiler_phase.html
new file mode 100644
index 00000000..8ca631e8
--- /dev/null
+++ b/src/compiler_phase.html
@@ -0,0 +1,33 @@
+<html>
+<body>
+<h2>Run the emscripten compiler in a web page, just for laughs</h2>
+Open the web console to see stderr output
+<hr>
+<pre id="output"></pre>
+<script>
+ arguments = ['tmp/emscripten_temp/tmpbTF9CI.txt', 'tmp/emscripten_temp/tmpz8Yvie.pre.ll', 'pre']; // copy from emscripten.py output
+
+ var outputElement = document.getElementById('output');
+ print = function(x) {
+ outputElement.innerHTML += 'output hidden, profiling mode';
+ print = function(){};
+ //outputElement.innerHTML += x;
+ };
+
+ // For generated code
+ var Module = {
+ print: function(x) {
+ throw 'what?'
+ }
+ };
+
+ var startTime = Date.now();
+</script>
+<script src="compiler.js">
+</script>
+<script>
+ outputElement.innerHTML += '<br>total time: ' + (Date.now() - startTime);
+</script>
+</body>
+</html>
+
diff --git a/src/framework.js b/src/framework.js
deleted file mode 100644
index 1a98ca16..00000000
--- a/src/framework.js
+++ /dev/null
@@ -1,257 +0,0 @@
-//"use strict";
-
-//
-// A framework to make building Emscripten easier. Lets you write modular
-// code to handle specific issues.
-//
-// Actor - Some code that processes items.
-// Item - Some data that is processed by actors.
-// Substrate - A 'world' with some actors and some items.
-//
-// The idea is to create a substrate, fill it with the proper items
-// and actors, and then run it to completion. Actors will process
-// the items they are given, and forward the results to other actors,
-// until we are finished. Some of the results are then the final
-// output of the entire calculation done in the substrate.
-//
-
-var DEBUG = 0;
-var DEBUG_MEMORY = 0;
-
-var MemoryDebugger = {
- clear: function() {
- MemoryDebugger.time = Date.now();
- MemoryDebugger.datas = {};
- var info = MemoryDebugger.doGC();
- MemoryDebugger.last = info[2];
- MemoryDebugger.max = 0;
- MemoryDebugger.tick('--clear--');
- },
-
- doGC: function() {
- var raw = gc().replace('\n', '');
- print('zz raw gc info: ' + raw);
- return /before (\d+), after (\d+),.*/.exec(raw);
- },
-
- tick: function(name) {
- var now = Date.now();
- if (now - this.time > 1000) {
- // ..
- this.time = now;
- }
-
- // assume |name| exists from now on
-
- var raw = gc().replace('\n', '');
- var info = MemoryDebugger.doGC();
- var before = info[1];
- var after = info[2];
- MemoryDebugger.max = Math.max(MemoryDebugger.max, before, after);
- // A GC not called by us may have done some work 'silently'
- var garbage = before - after;
- var real = after - MemoryDebugger.last;
- print('zz gc stats changes: ' + [name, real, garbage]);
- MemoryDebugger.last = after;
-
- if (Math.abs(garbage) + Math.abs(real) > 0) {
- var data = MemoryDebugger.datas[name];
- if (!data) {
- data = MemoryDebugger.datas[name] = {
- name: name,
- count: 0,
- garbage: 0,
- real: 0
- };
- }
- data.garbage += garbage;
- data.real += real;
- data.count++;
- }
-
- MemoryDebugger.dump();
- },
-
- dump: function() {
- var vals = values(MemoryDebugger.datas);
- print('zz max: ' + (MemoryDebugger.max/(1024*1024)).toFixed(3));
- print('zz real:');
- vals.sort(function(x, y) { return y.real - x.real });
- vals.forEach(function(v) { if (Math.abs(v.real) > 1024*1024) print('zz ' + v.name + ' real = ' + (v.real/(1024*1024)).toFixed(3) + ' mb'); });
- print('zz garbage:');
- vals.sort(function(x, y) { return y.garbage - x.garbage });
- vals.forEach(function(v) { if (Math.abs(v.garbage) > 1024*1024) print('zz ' + v.name + ' garbage = ' + (v.garbage/(1024*1024)).toFixed(3) + ' mb'); });
- }
-}
-
-if (DEBUG_MEMORY) MemoryDebugger.clear();
-
-function Substrate(name_) {
- this.name_ = name_;
- this.actors = {};
- this.currUid = 1;
-};
-
-Substrate.prototype = {
- addItem: function(item, targetActor) {
- if (targetActor == '/dev/null') return;
- if (targetActor == '/dev/stdout') {
- this.results.push(item);
- return;
- }
- this.actors[targetActor].inbox.push(item);
- },
-
- addItems: function(items, targetActor) {
- if (targetActor == '/dev/null') return;
- if (targetActor == '/dev/stdout') {
- this.results = this.results.concat(items);
- return;
- }
- this.actors[targetActor].inbox = this.actors[targetActor].inbox.concat(items);
- },
-
- checkInbox: function(actor) {
- var items = actor.inbox;
- for (var i = 0; i < items.length; i++) {
- var item = items[i];
- if (!item.__uid__) {
- item.__uid__ = this.currUid;
- this.currUid ++;
- }
- }
- var MAX_INCOMING = Infinity;
- if (MAX_INCOMING == Infinity) {
- actor.inbox = [];
- actor.items = actor.items.concat(items);
- } else {
- throw 'Warning: Enter this code at your own risk. It can save memory, but often regresses speed.';
- actor.inbox = items.slice(MAX_INCOMING);
- actor.items = actor.items.concat(items.slice(0, MAX_INCOMING));
- }
- },
-
- addActor: function(name_, actor) {
- assert(name_ && actor);
- actor.name_ = name_;
- actor.items = [];
- actor.inbox = [];
- actor.forwardItem = bind(this, this.addItem);
- actor.forwardItems = bind(this, this.addItems);
- this.actors[name_] = actor;
- if (!actor.process) actor.process = Actor.prototype.process;
- return actor;
- },
-
- solve: function() {
- dprint('framework', "// Solving " + this.name_ + "...");
-
- if (DEBUG_MEMORY) MemoryDebugger.tick('pre-run substrate ' + this.name_ + ' (priors may be cleaned)');
-
- var startTime = Date.now();
- var midTime = startTime;
- var that = this;
- function midComment() {
- var curr = Date.now();
- if (curr - midTime > 500) {
- dprint('framework', '// Working on ' + that.name_ + ', so far ' + ((curr-startTime)/1000).toString().substr(0,10) + ' seconds.');
- midTime = curr;
- }
- }
- function finalComment() {
- dprint('framework', '// Completed ' + that.name_ + ' in ' + ((Date.now() - startTime)/1000).toString().substr(0,10) + ' seconds.');
- }
-
- var ret = null;
- var finalResult = null;
- this.results = [];
- var finished = false;
- var onResult = this.onResult;
- var actors = values(this.actors); // Assumes actors are not constantly added
- while (!finished) {
- dprint('framework', "Cycle start, items: ");// + values(this.actors).map(function(actor) actor.items).reduce(function(x,y) x+y, 0));
- var hadProcessing = false;
- for (var i = 0; i < actors.length; i++) {
- var actor = actors[i];
- if (actor.inbox.length == 0 && actor.items.length == 0) continue;
-
- midComment();
-
- this.checkInbox(actor);
- var outputs;
- var currResultCount = this.results.length;
- dprint('framework', 'Processing using ' + actor.name_ + ': ' + actor.items.length);
- outputs = actor.process(actor.items);
- actor.items = [];
- if (DEBUG_MEMORY) MemoryDebugger.tick('actor ' + actor.name_);
- dprint('framework', 'New results: ' + (outputs.length + this.results.length - currResultCount) + ' out of ' + (this.results.length + outputs.length));
- hadProcessing = true;
-
- if (outputs && outputs.length > 0) {
- if (outputs.length === 1 && outputs[0].__finalResult__) {
- if (DEBUG) print("Solving complete: __finalResult__");
- delete outputs[0].__finalResult__; // Might recycle this
- delete outputs[0].__uid__;
- finalComment();
- finished = true;
- finalResult = outputs[0];
- } else {
- this.results = this.results.concat(outputs);
- }
- }
- }
- if (!hadProcessing) {
- if (DEBUG) print("Solving complete: no remaining items");
- finalComment();
- this.results.forEach(function(result) {
- delete result.__uid__; // Might recycle these
- if (onResult) onResult(result);
- });
- ret = this.results;
- this.results = null; // No need to hold on to them any more
- break;
- }
- if (finalResult) {
- ret = finalResult;
- break;
- }
- midComment();
- }
-
- // Clear the actors. Do not forget about the actors, though, to make this substrate reusable.
- actors.forEach(function(actor) {
- actor.items = null;
- actor.inbox = null;
- });
-
- if (DEBUG_MEMORY) MemoryDebugger.tick('finished ' + this.name_);
-
- return ret;
- }
-};
-
-// Global access to the currently-being processed item.
-// Note that if you overload process in Actor, this will need to be set if you rely on it.
-var Framework = {
- currItem: null
-};
-
-function Actor() { };
-Actor.prototype = {
- process: function(items) {
- var ret = [];
- for (var i = 0; i < items.length; i++) {
- var item = items[i];
- items[i] = null; // items may be very very large. Allow GCing to occur in the loop by releasing refs here
- dprint('frameworkLines', 'Processing item for llvm line ' + item.lineNum);
- Framework.currItem = item;
- var outputs = this.processItem(item);
- Framework.currItem = null;
- if (outputs) {
- ret = ret.concat(outputs);
- }
- }
- return ret;
- }
-};
-
diff --git a/src/intertyper.js b/src/intertyper.js
index 082fd993..07f2020c 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -3,10 +3,151 @@
// LLVM assembly => internal intermediate representation, which is ready
// to be processed by the later stages.
-var tokenizer; // TODO: Clean this up/out
- // XXX In particular, this closes over the substrate, which can keep stuff in memory, which is bad
+var fastPaths = 0, slowPaths = 0;
+
+// Line tokenizer
+function tokenizer(item, inner) {
+ //assert(item.lineNum != 40000);
+ //if (item.lineNum) print(item.lineNum);
+ var tokens = [];
+ var quotes = 0;
+ var lastToken = null;
+ var CHUNKSIZE = 64; // How much forward to peek forward. Too much means too many string segments copied
+ // Note: '{' is not an encloser, as its use in functions is split over many lines
+ var enclosers = {
+ '[': 0,
+ ']': '[',
+ '(': 0,
+ ')': '(',
+ '<': 0,
+ '>': '<'
+ };
+ var totalEnclosing = 0;
+ function makeToken(text) {
+ if (text.length == 0) return;
+ // merge certain tokens
+ if (lastToken && ( (lastToken.text == '%' && text[0] == '"') || /^\**$/.test(text) ) ) {
+ lastToken.text += text;
+ return;
+ }
+
+ var token = {
+ text: text
+ };
+ if (text[0] in enclosers) {
+ token.item = tokenizer({
+ lineText: text.substr(1, text.length-2)
+ }, true);
+ token.type = text[0];
+ }
+ // merge certain tokens
+ if (lastToken && isType(lastToken.text) && isFunctionDef(token)) {
+ lastToken.text += ' ' + text;
+ } else if (lastToken && text[0] == '}') { // }, }*, etc.
+ var openBrace = tokens.length-1;
+ while (tokens[openBrace].text.substr(-1) != '{') openBrace --;
+ token = combineTokens(tokens.slice(openBrace+1));
+ tokens.splice(openBrace, tokens.length-openBrace+1);
+ tokens.push(token);
+ token.type = '{';
+ token.text = '{ ' + token.text + ' }';
+ var pointingLevelsToAdd = pointingLevels(text) - pointingLevels(token.text);
+ while (pointingLevelsToAdd > 0) {
+ token.text += '*';
+ pointingLevelsToAdd--;
+ }
+ lastToken = token;
+ } else {
+ tokens.push(token);
+ lastToken = token;
+ }
+ }
+ // Split using meaningful characters
+ var lineText = item.lineText + ' ';
+ var re = /[\[\]\(\)<>, "]/g;
+ var segments = lineText.split(re);
+ segments.pop();
+ var len = segments.length;
+ var i = -1;
+ var curr = '';
+ var segment, letter;
+ for (var s = 0; s < len; s++) {
+ segment = segments[s];
+ i += segment.length + 1;
+ letter = lineText[i];
+ curr += segment;
+ switch (letter) {
+ case ' ':
+ if (totalEnclosing == 0 && quotes == 0) {
+ makeToken(curr);
+ curr = '';
+ } else {
+ curr += ' ';
+ }
+ break;
+ case '"':
+ if (totalEnclosing == 0) {
+ if (quotes == 0) {
+ if (curr == '@' || curr == '%') {
+ curr += '"';
+ } else {
+ makeToken(curr);
+ curr = '"';
+ }
+ } else {
+ makeToken(curr + '"');
+ curr = '';
+ }
+ } else {
+ curr += '"';
+ }
+ quotes = 1-quotes;
+ break;
+ case ',':
+ if (totalEnclosing == 0 && quotes == 0) {
+ makeToken(curr);
+ curr = '';
+ tokens.push({ text: ',' });
+ } else {
+ curr += ',';
+ }
+ break;
+ default:
+ assert(letter in enclosers);
+ if (quotes) {
+ curr += letter;
+ break;
+ }
+ if (letter in ENCLOSER_STARTERS) {
+ if (totalEnclosing == 0) {
+ makeToken(curr);
+ curr = '';
+ }
+ curr += letter;
+ enclosers[letter]++;
+ totalEnclosing++;
+ } else {
+ enclosers[enclosers[letter]]--;
+ totalEnclosing--;
+ if (totalEnclosing == 0) {
+ makeToken(curr + letter);
+ curr = '';
+ } else {
+ curr += letter;
+ }
+ }
+ }
+ }
+ var newItem = {
+ tokens: tokens,
+ indent: lineText.search(/[^ ]/),
+ lineNum: item.lineNum
+ };
+ return newItem;
+}
+
function tokenize(text) {
- return tokenizer.processItem({ lineText: text }, true);
+ return tokenizer({ lineText: text }, true);
}
// Handy sets
@@ -22,672 +163,490 @@ var NSW_NUW = set('nsw', 'nuw');
// Intertyper
-function intertyper(data, sidePass, baseLineNums) {
+function intertyper(lines, sidePass, baseLineNums) {
var mainPass = !sidePass;
baseLineNums = baseLineNums || [[0,0]]; // each pair [#0,#1] means "starting from line #0, the base line num is #1"
dprint('framework', 'Big picture: Starting intertyper, main pass=' + mainPass);
- // Substrate
-
- var substrate = new Substrate('Intertyper');
+ var finalResults = [];
- // Line splitter. We break off some bunches of lines into unparsedBundles, which are
+ // Line splitter. We break off some bunches of lines into unparsed bundles, which are
// parsed in separate passes later. This helps to keep memory usage low - we can start
// from raw lines and end up with final JS for each function individually that way, instead
// of intertyping them all, then analyzing them all, etc.
- substrate.addActor('LineSplitter', {
- processItem: function _lineSplitter(item) {
- var lines = item.llvmLines;
- var ret = [];
- var inContinual = false;
- var inFunction = false;
- var currFunctionLines;
- var currFunctionLineNum;
- var unparsedBundles = [];
- var unparsedTypes, unparsedGlobals;
- if (mainPass) {
- unparsedTypes = {
- intertype: 'unparsedTypes',
- lines: []
- };
- unparsedBundles.push(unparsedTypes);
- unparsedGlobals = {
- intertype: 'unparsedGlobals',
- lines: []
- };
- unparsedBundles.push(unparsedGlobals);
- }
- var baseLineNumPosition = 0;
- for (var i = 0; i < lines.length; i++) {
- var line = lines[i];
- if (singlePhase) lines[i] = null; // lines may be very very large. Allow GCing to occur in the loop by releasing refs here
+ function lineSplitter() {
+ var ret = [];
+ var inContinual = false;
+ var inFunction = false;
+ var currFunctionLines;
+ var currFunctionLineNum;
+ var unparsedTypes, unparsedGlobals;
+ if (mainPass) {
+ unparsedTypes = {
+ intertype: 'unparsedTypes',
+ lines: []
+ };
+ finalResults.push(unparsedTypes);
+ unparsedGlobals = {
+ intertype: 'unparsedGlobals',
+ lines: []
+ };
+ finalResults.push(unparsedGlobals);
+ }
+ var baseLineNumPosition = 0;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (singlePhase) lines[i] = null; // lines may be very very large. Allow GCing to occur in the loop by releasing refs here
- while (baseLineNumPosition < baseLineNums.length-1 && i >= baseLineNums[baseLineNumPosition+1][0]) {
- baseLineNumPosition++;
- }
+ while (baseLineNumPosition < baseLineNums.length-1 && i >= baseLineNums[baseLineNumPosition+1][0]) {
+ baseLineNumPosition++;
+ }
- if (mainPass && (line[0] == '%' || line[0] == '@')) {
- // If this isn't a type, it's a global variable, make a note of the information now, we will need it later
- var parts = line.split(' = ');
- assert(parts.length >= 2);
- var left = parts[0], right = parts.slice(1).join(' = ');
- var testType = /^type .*/.exec(right);
- if (!testType) {
- var globalIdent = toNiceIdent(left);
- var testAlias = /^(hidden )?alias .*/.exec(right);
- Variables.globals[globalIdent] = {
- name: globalIdent,
- alias: !!testAlias,
- impl: VAR_EMULATED
- };
- unparsedGlobals.lines.push(line);
- } else {
- unparsedTypes.lines.push(line);
- }
- continue;
- }
- if (mainPass && /^define .*/.test(line)) {
- inFunction = true;
- currFunctionLines = [];
- currFunctionLineNum = i + 1;
+ if (mainPass && (line[0] == '%' || line[0] == '@')) {
+ // If this isn't a type, it's a global variable, make a note of the information now, we will need it later
+ var parts = line.split(' = ');
+ assert(parts.length >= 2);
+ var left = parts[0], right = parts.slice(1).join(' = ');
+ var testType = /^type .*/.exec(right);
+ if (!testType) {
+ var globalIdent = toNiceIdent(left);
+ var testAlias = /^(hidden )?alias .*/.exec(right);
+ Variables.globals[globalIdent] = {
+ name: globalIdent,
+ alias: !!testAlias,
+ impl: VAR_EMULATED
+ };
+ unparsedGlobals.lines.push(line);
+ } else {
+ unparsedTypes.lines.push(line);
}
- if (!inFunction || !mainPass) {
- if (inContinual || /^\ +(to|catch |filter |cleanup).*/.test(line)) {
- // to after invoke or landingpad second line
- ret.slice(-1)[0].lineText += line;
- if (/^\ +\]/.test(line)) { // end of llvm switch
- inContinual = false;
- }
- } else {
- ret.push({
- lineText: line,
- lineNum: i + 1 + baseLineNums[baseLineNumPosition][1] - baseLineNums[baseLineNumPosition][0]
- });
- if (/^\ +switch\ .*/.test(line)) {
- // beginning of llvm switch
- inContinual = true;
- }
+ continue;
+ }
+ if (mainPass && /^define .*/.test(line)) {
+ inFunction = true;
+ currFunctionLines = [];
+ currFunctionLineNum = i + 1;
+ }
+ if (!inFunction || !mainPass) {
+ if (inContinual || /^\ +(to|catch |filter |cleanup).*/.test(line)) {
+ // to after invoke or landingpad second line
+ ret.slice(-1)[0].lineText += line;
+ if (/^\ +\]/.test(line)) { // end of llvm switch
+ inContinual = false;
}
} else {
- currFunctionLines.push(line);
- }
- if (mainPass && /^}.*/.test(line)) {
- inFunction = false;
- if (mainPass) {
- var func = funcHeader.processItem(tokenizer.processItem({ lineText: currFunctionLines[0], lineNum: currFunctionLineNum }, true))[0];
-
- if (SKIP_STACK_IN_SMALL && /emscripten_autodebug/.exec(func.ident)) {
- warnOnce('Disabling SKIP_STACK_IN_SMALL because we are apparently processing autodebugger data');
- SKIP_STACK_IN_SMALL = 0;
- }
-
- var ident = toNiceIdent(func.ident);
- if (!(ident in DEAD_FUNCTIONS)) {
- unparsedBundles.push({
- intertype: 'unparsedFunction',
- // We need this early, to know basic function info - ident, params, varargs
- ident: ident,
- params: func.params,
- returnType: func.returnType,
- hasVarArgs: func.hasVarArgs,
- lineNum: currFunctionLineNum,
- lines: currFunctionLines
- });
- }
- currFunctionLines = [];
+ ret.push({
+ lineText: line,
+ lineNum: i + 1 + baseLineNums[baseLineNumPosition][1] - baseLineNums[baseLineNumPosition][0]
+ });
+ if (/^\ +switch\ .*/.test(line)) {
+ // beginning of llvm switch
+ inContinual = true;
}
}
+ } else {
+ currFunctionLines.push(line);
}
- // We need lines beginning with ';' inside functions, because older LLVM versions generated labels that way. But when not
- // parsing functions, we can ignore all such lines and save some time that way.
- this.forwardItems(ret.filter(function(item) { return item.lineText && (item.lineText[0] != ';' || !mainPass); }), 'Tokenizer');
- return unparsedBundles;
- }
- });
+ if (mainPass && /^}.*/.test(line)) {
+ inFunction = false;
+ if (mainPass) {
+ var func = funcHeaderHandler(tokenizer({ lineText: currFunctionLines[0], lineNum: currFunctionLineNum }, true));
- // Line tokenizer
- tokenizer = substrate.addActor('Tokenizer', {
- processItem: function _tokenizer(item, inner) {
- //assert(item.lineNum != 40000);
- //if (item.lineNum) print(item.lineNum);
- var tokens = [];
- var quotes = 0;
- var lastToken = null;
- var CHUNKSIZE = 64; // How much forward to peek forward. Too much means too many string segments copied
- // Note: '{' is not an encloser, as its use in functions is split over many lines
- var enclosers = {
- '[': 0,
- ']': '[',
- '(': 0,
- ')': '(',
- '<': 0,
- '>': '<'
- };
- var totalEnclosing = 0;
- var that = this;
- function makeToken(text) {
- if (text.length == 0) return;
- // merge certain tokens
- if (lastToken && ( (lastToken.text == '%' && text[0] == '"') || /^\**$/.test(text) ) ) {
- lastToken.text += text;
- return;
- }
+ if (SKIP_STACK_IN_SMALL && /emscripten_autodebug/.exec(func.ident)) {
+ warnOnce('Disabling SKIP_STACK_IN_SMALL because we are apparently processing autodebugger data');
+ SKIP_STACK_IN_SMALL = 0;
+ }
- var token = {
- text: text
- };
- if (text[0] in enclosers) {
- token.item = that.processItem({
- lineText: text.substr(1, text.length-2)
- }, true);
- token.type = text[0];
- }
- // merge certain tokens
- if (lastToken && isType(lastToken.text) && isFunctionDef(token)) {
- lastToken.text += ' ' + text;
- } else if (lastToken && text[0] == '}') { // }, }*, etc.
- var openBrace = tokens.length-1;
- while (tokens[openBrace].text.substr(-1) != '{') openBrace --;
- token = combineTokens(tokens.slice(openBrace+1));
- tokens.splice(openBrace, tokens.length-openBrace+1);
- tokens.push(token);
- token.type = '{';
- token.text = '{ ' + token.text + ' }';
- var pointingLevelsToAdd = pointingLevels(text) - pointingLevels(token.text);
- while (pointingLevelsToAdd > 0) {
- token.text += '*';
- pointingLevelsToAdd--;
+ var ident = toNiceIdent(func.ident);
+ if (!(ident in DEAD_FUNCTIONS)) {
+ finalResults.push({
+ intertype: 'unparsedFunction',
+ // We need this early, to know basic function info - ident, params, varargs
+ ident: ident,
+ params: func.params,
+ returnType: func.returnType,
+ hasVarArgs: func.hasVarArgs,
+ lineNum: currFunctionLineNum,
+ lines: currFunctionLines
+ });
}
- lastToken = token;
- } else {
- tokens.push(token);
- lastToken = token;
- }
- }
- // Split using meaningful characters
- var lineText = item.lineText + ' ';
- var re = /[\[\]\(\)<>, "]/g;
- var segments = lineText.split(re);
- segments.pop();
- var len = segments.length;
- var i = -1;
- var curr = '';
- var segment, letter;
- for (var s = 0; s < len; s++) {
- segment = segments[s];
- i += segment.length + 1;
- letter = lineText[i];
- curr += segment;
- switch (letter) {
- case ' ':
- if (totalEnclosing == 0 && quotes == 0) {
- makeToken(curr);
- curr = '';
- } else {
- curr += ' ';
- }
- break;
- case '"':
- if (totalEnclosing == 0) {
- if (quotes == 0) {
- if (curr == '@' || curr == '%') {
- curr += '"';
- } else {
- makeToken(curr);
- curr = '"';
- }
- } else {
- makeToken(curr + '"');
- curr = '';
- }
- } else {
- curr += '"';
- }
- quotes = 1-quotes;
- break;
- case ',':
- if (totalEnclosing == 0 && quotes == 0) {
- makeToken(curr);
- curr = '';
- tokens.push({ text: ',' });
- } else {
- curr += ',';
- }
- break;
- default:
- assert(letter in enclosers);
- if (quotes) {
- curr += letter;
- break;
- }
- if (letter in ENCLOSER_STARTERS) {
- if (totalEnclosing == 0) {
- makeToken(curr);
- curr = '';
- }
- curr += letter;
- enclosers[letter]++;
- totalEnclosing++;
- } else {
- enclosers[enclosers[letter]]--;
- totalEnclosing--;
- if (totalEnclosing == 0) {
- makeToken(curr + letter);
- curr = '';
- } else {
- curr += letter;
- }
- }
+ currFunctionLines = [];
}
}
- var newItem = {
- tokens: tokens,
- indent: lineText.search(/[^ ]/),
- lineNum: item.lineNum
- };
- if (inner) {
- return newItem;
- } else {
- this.forwardItem(newItem, 'Triager');
- }
- return null;
}
- });
+ // We need lines beginning with ';' inside functions, because older LLVM versions generated labels that way. But when not
+ // parsing functions, we can ignore all such lines and save some time that way.
+ return ret.filter(function(item) { return item.lineText && (item.lineText[0] != ';' || !mainPass); });
+ }
- substrate.addActor('Triager', {
- processItem: function _triager(item) {
- function triage() {
- assert(!item.intertype);
- var token0Text = item.tokens[0].text;
- var token1Text = item.tokens[1] ? item.tokens[1].text : null;
- var tokensLength = item.tokens.length;
- if (item.indent === 2) {
- if (tokensLength >= 5 &&
- (token0Text == 'store' || token1Text == 'store'))
- return 'Store';
- if (tokensLength >= 3 && token0Text == 'br')
- return 'Branch';
- if (tokensLength >= 2 && token0Text == 'ret')
- return 'Return';
- if (tokensLength >= 2 && token0Text == 'switch')
- return 'Switch';
- if (token0Text == 'unreachable')
- return 'Unreachable';
- if (tokensLength >= 3 && token0Text == 'indirectbr')
- return 'IndirectBr';
- if (tokensLength >= 2 && token0Text == 'resume')
- return 'Resume';
- if (tokensLength >= 3 &&
- (token0Text == 'load' || token1Text == 'load'))
- return 'Load';
- if (tokensLength >= 3 &&
- token0Text in MATHOPS)
- return 'Mathops';
- if (tokensLength >= 3 && token0Text == 'bitcast')
- return 'Bitcast';
- if (tokensLength >= 3 && token0Text == 'getelementptr')
- return 'GEP';
- if (tokensLength >= 2 && token0Text == 'alloca')
- return 'Alloca';
- if (tokensLength >= 3 && token0Text == 'extractvalue')
- return 'ExtractValue';
- if (tokensLength >= 3 && token0Text == 'insertvalue')
- return 'InsertValue';
- if (tokensLength >= 3 && token0Text == 'phi')
- return 'Phi';
- if (tokensLength >= 3 && token0Text == 'va_arg')
- return 'va_arg';
- if (tokensLength >= 3 && token0Text == 'landingpad')
- return 'Landingpad';
- if (token0Text == 'fence')
- return '/dev/null';
- } else if (item.indent === 0) {
- if ((tokensLength >= 1 && token0Text.substr(-1) == ':') ||
- (tokensLength >= 3 && token1Text == '<label>') ||
- (tokensLength >= 2 && token1Text == ':'))
- return 'Label';
- if (tokensLength >= 4 && token0Text == 'declare')
- return 'External';
- if (tokensLength >= 3 && token1Text == '=')
- return 'Global';
- if (tokensLength >= 4 && token0Text == 'define' &&
- item.tokens.slice(-1)[0].text == '{')
- return 'FuncHeader';
- if (tokensLength >= 1 && token0Text == '}')
- return 'FuncEnd';
- if (token0Text == 'module' && token1Text == 'asm') {
- warn('Ignoring module asm: ' + item.tokens[2].text);
- return '/dev/null';
- }
- if (token0Text == 'attributes')
- return '/dev/null';
- }
- if (tokensLength >= 3 && (token0Text == 'call' || token1Text == 'call'))
- return 'Call';
- if (token0Text == 'target') {
- if (token1Text == 'triple') {
- var triple = item.tokens[3].text;
- triple = triple.substr(1, triple.length-2);
- var expected = TARGET_LE32 ? 'le32-unknown-nacl' : 'i386-pc-linux-gnu';
- if (triple !== expected) {
- warn('using an unexpected LLVM triple: ' + [triple, ' !== ', expected] + ' (are you using emcc for everything and not clang?)');
- }
- }
- return '/dev/null';
- }
- if (token0Text == ';')
- return '/dev/null';
- if (tokensLength >= 3 && token0Text == 'invoke')
- return 'Invoke';
- if (tokensLength >= 3 && token0Text == 'atomicrmw' || token0Text == 'cmpxchg')
- return 'Atomic';
- throw 'Invalid token, cannot triage: ' + dump(item);
+ function triager(item) {
+ assert(!item.intertype);
+ if (item.indent == 2 && (eq = findTokenText(item, '=')) >= 0) {
+ item.assignTo = toNiceIdent(combineTokens(item.tokens.slice(0, eq)).text);
+ item.tokens = item.tokens.slice(eq+1);
+ }
+ var token0Text = item.tokens[0].text;
+ var token1Text = item.tokens[1] ? item.tokens[1].text : null;
+ var tokensLength = item.tokens.length;
+ if (item.indent === 2) {
+ if (tokensLength >= 5 &&
+ (token0Text == 'store' || token1Text == 'store'))
+ return storeHandler(item);
+ if (tokensLength >= 3 && token0Text == 'br')
+ return branchHandler(item);
+ if (tokensLength >= 2 && token0Text == 'ret')
+ return returnHandler(item);
+ if (tokensLength >= 2 && token0Text == 'switch')
+ return switchHandler(item);
+ if (token0Text == 'unreachable')
+ return unreachableHandler(item);
+ if (tokensLength >= 3 && token0Text == 'indirectbr')
+ return indirectBrHandler(item);
+ if (tokensLength >= 2 && token0Text == 'resume')
+ return resumeHandler(item);
+ if (tokensLength >= 3 &&
+ (token0Text == 'load' || token1Text == 'load'))
+ return loadHandler(item);
+ if (tokensLength >= 3 &&
+ token0Text in MATHOPS)
+ return mathopsHandler(item);
+ if (tokensLength >= 3 && token0Text == 'bitcast')
+ return bitcastHandler(item);
+ if (tokensLength >= 3 && token0Text == 'getelementptr')
+ return GEPHandler(item);
+ if (tokensLength >= 2 && token0Text == 'alloca')
+ return allocaHandler(item);
+ if (tokensLength >= 3 && token0Text == 'extractvalue')
+ return extractValueHandler(item);
+ if (tokensLength >= 3 && token0Text == 'insertvalue')
+ return insertValueHandler(item);
+ if (tokensLength >= 3 && token0Text == 'phi')
+ return phiHandler(item);
+ if (tokensLength >= 3 && token0Text == 'va_arg')
+ return va_argHandler(item);
+ if (tokensLength >= 3 && token0Text == 'landingpad')
+ return landingpadHandler(item);
+ if (token0Text == 'fence')
+ return null;
+ } else if (item.indent === 0) {
+ if ((tokensLength >= 1 && token0Text.substr(-1) == ':') ||
+ (tokensLength >= 3 && token1Text == '<label>') ||
+ (tokensLength >= 2 && token1Text == ':'))
+ return labelHandler(item);
+ if (tokensLength >= 4 && token0Text == 'declare')
+ return externalHandler(item);
+ if (tokensLength >= 3 && token1Text == '=')
+ return globalHandler(item);
+ if (tokensLength >= 4 && token0Text == 'define' &&
+ item.tokens.slice(-1)[0].text == '{')
+ return funcHeaderHandler(item);
+ if (tokensLength >= 1 && token0Text == '}')
+ return funcEndHandler(item);
+ if (token0Text == 'module' && token1Text == 'asm') {
+ warn('Ignoring module asm: ' + item.tokens[2].text);
+ return null;
}
- var eq;
- if (item.indent == 2 && (eq = findTokenText(item, '=')) >= 0) {
- item.assignTo = toNiceIdent(combineTokens(item.tokens.slice(0, eq)).text);
- item.tokens = item.tokens.slice(eq+1);
+ if (token0Text == 'attributes')
+ return null;
+ }
+ if (tokensLength >= 3 && (token0Text == 'call' || token1Text == 'call'))
+ return callHandler(item);
+ if (token0Text == 'target') {
+ if (token1Text == 'triple') {
+ var triple = item.tokens[3].text;
+ triple = triple.substr(1, triple.length-2);
+ var expected = TARGET_LE32 ? 'le32-unknown-nacl' : 'i386-pc-linux-gnu';
+ if (triple !== expected) {
+ warn('using an unexpected LLVM triple: ' + [triple, ' !== ', expected] + ' (are you using emcc for everything and not clang?)');
+ }
}
- this.forwardItem(item, triage());
+ return null;
}
- });
+ if (token0Text == ';')
+ return null;
+ if (tokensLength >= 3 && token0Text == 'invoke')
+ return invokeHandler(item);
+ if (tokensLength >= 3 && token0Text == 'atomicrmw' || token0Text == 'cmpxchg')
+ return atomicHandler(item);
+ throw 'Invalid token, cannot triage: ' + dump(item);
+ }
// Line parsers to intermediate form
// globals: type or variable
- substrate.addActor('Global', {
- processItem: function _global(item) {
- function scanConst(value, type) {
- // Gets an array of constant items, separated by ',' tokens
- function handleSegments(tokens) {
- // Handle a single segment (after comma separation)
- function handleSegment(segment) {
- if (segment[1].text == 'null') {
- return { intertype: 'value', ident: '0', type: 'i32' };
- } else if (segment[1].text == 'zeroinitializer') {
- Types.needAnalysis[segment[0].text] = 0;
- return { intertype: 'emptystruct', type: segment[0].text };
- } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) {
- return parseLLVMFunctionCall(segment);
- } else if (segment[1].type && segment[1].type == '{') {
- Types.needAnalysis[segment[0].text] = 0;
- return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].tokens) };
- } else if (segment[1].type && segment[1].type == '<') {
- Types.needAnalysis[segment[0].text] = 0;
- return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].item.tokens[0].tokens) };
- } else if (segment[1].type && segment[1].type == '[') {
- Types.needAnalysis[segment[0].text] = 0;
- return { intertype: 'list', type: segment[0].text, contents: handleSegments(segment[1].item.tokens) };
- } else if (segment.length == 2) {
- Types.needAnalysis[segment[0].text] = 0;
- return { intertype: 'value', type: segment[0].text, ident: toNiceIdent(segment[1].text) };
- } else if (segment[1].text === 'c') {
- // string
- var text = segment[2].text;
- text = text.substr(1, text.length-2);
- return { intertype: 'string', text: text, type: 'i8*' };
- } else if (segment[1].text === 'blockaddress') {
- return parseBlockAddress(segment);
- } else {
- throw 'Invalid segment: ' + dump(segment);
- }
- };
- return splitTokenList(tokens).map(handleSegment);
- }
+ function noteGlobalVariable(ret) {
+ if (!NAMED_GLOBALS) {
+ Variables.globals[ret.ident].type = ret.type;
+ Variables.globals[ret.ident].external = ret.external;
+ }
+ Types.needAnalysis[ret.type] = 0;
+ }
- Types.needAnalysis[type] = 0;
- if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) {
- return { value: toNiceIdent(value.text), type: type };
- } else if (value.text in ZEROINIT_UNDEF) { // undef doesn't really need initting, but why not
- return { intertype: 'emptystruct', type: type };
- } else if (value.text && value.text[0] == '"') {
- return { intertype: 'string', text: value.text.substr(1, value.text.length-2) };
- } else {
- if (value.type == '<') { // <{ i8 }> etc.
- value = value.item.tokens;
- }
- var contents;
- if (value.item) {
- // list of items
- contents = value.item.tokens;
- } else if (value.type == '{') {
- // struct
- contents = value.tokens;
- } else if (value[0]) {
- contents = value[0];
+ function globalHandler(item) {
+ function scanConst(value, type) {
+ // Gets an array of constant items, separated by ',' tokens
+ function handleSegments(tokens) {
+ // Handle a single segment (after comma separation)
+ function handleSegment(segment) {
+ if (segment[1].text == 'null') {
+ return { intertype: 'value', ident: '0', type: 'i32' };
+ } else if (segment[1].text == 'zeroinitializer') {
+ Types.needAnalysis[segment[0].text] = 0;
+ return { intertype: 'emptystruct', type: segment[0].text };
+ } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) {
+ return parseLLVMFunctionCall(segment);
+ } else if (segment[1].type && segment[1].type == '{') {
+ Types.needAnalysis[segment[0].text] = 0;
+ return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].tokens) };
+ } else if (segment[1].type && segment[1].type == '<') {
+ Types.needAnalysis[segment[0].text] = 0;
+ return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].item.tokens[0].tokens) };
+ } else if (segment[1].type && segment[1].type == '[') {
+ Types.needAnalysis[segment[0].text] = 0;
+ return { intertype: 'list', type: segment[0].text, contents: handleSegments(segment[1].item.tokens) };
+ } else if (segment.length == 2) {
+ Types.needAnalysis[segment[0].text] = 0;
+ return { intertype: 'value', type: segment[0].text, ident: toNiceIdent(segment[1].text) };
+ } else if (segment[1].text === 'c') {
+ // string
+ var text = segment[2].text;
+ text = text.substr(1, text.length-2);
+ return { intertype: 'string', text: text, type: 'i8*' };
+ } else if (segment[1].text === 'blockaddress') {
+ return parseBlockAddress(segment);
} else {
- throw '// interfailzzzzzzzzzzzzzz ' + dump(value.item) + ' ::: ' + dump(value);
+ throw 'Invalid segment: ' + dump(segment);
}
- return { intertype: 'segments', contents: handleSegments(contents) };
- }
+ };
+ return splitTokenList(tokens).map(handleSegment);
}
- cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 2);
- if (item.tokens[2].text == 'alias') {
- cleanOutTokens(LLVM.LINKAGES, item.tokens, 3);
- cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 3);
- var last = getTokenIndexByText(item.tokens, ';');
- var ret = {
- intertype: 'alias',
- ident: toNiceIdent(item.tokens[0].text),
- value: parseLLVMSegment(item.tokens.slice(3, last)),
- lineNum: item.lineNum
- };
- ret.type = ret.value.type;
- Types.needAnalysis[ret.type] = 0;
- if (!NAMED_GLOBALS) {
- Variables.globals[ret.ident].type = ret.type;
+ Types.needAnalysis[type] = 0;
+ if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) {
+ return { value: toNiceIdent(value.text), type: type };
+ } else if (value.text in ZEROINIT_UNDEF) { // undef doesn't really need initting, but why not
+ return { intertype: 'emptystruct', type: type };
+ } else if (value.text && value.text[0] == '"') {
+ return { intertype: 'string', text: value.text.substr(1, value.text.length-2) };
+ } else {
+ if (value.type == '<') { // <{ i8 }> etc.
+ value = value.item.tokens;
+ }
+ var contents;
+ if (value.item) {
+ // list of items
+ contents = value.item.tokens;
+ } else if (value.type == '{') {
+ // struct
+ contents = value.tokens;
+ } else if (value[0]) {
+ contents = value[0];
+ } else {
+ throw '// interfailzzzzzzzzzzzzzz ' + dump(value.item) + ' ::: ' + dump(value);
}
- return [ret];
+ return { intertype: 'segments', contents: handleSegments(contents) };
}
- if (item.tokens[2].text == 'type') {
- var fields = [];
- var packed = false;
- if (Runtime.isNumberType(item.tokens[3].text)) {
- // Clang sometimes has |= i32| instead of |= { i32 }|
- fields = [item.tokens[3].text];
- } else if (item.tokens[3].text != 'opaque') {
- if (item.tokens[3].type == '<') {
- packed = true;
- item.tokens[3] = item.tokens[3].item.tokens[0];
- }
- var subTokens = item.tokens[3].tokens;
- if (subTokens) {
- subTokens.push({text:','});
- while (subTokens[0]) {
- var stop = 1;
- while ([','].indexOf(subTokens[stop].text) == -1) stop ++;
- fields.push(combineTokens(subTokens.slice(0, stop)).text);
- subTokens.splice(0, stop+1);
- }
- }
+ }
+
+ cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 2);
+ if (item.tokens[2].text == 'alias') {
+ cleanOutTokens(LLVM.LINKAGES, item.tokens, 3);
+ cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 3);
+ var last = getTokenIndexByText(item.tokens, ';');
+ var ret = {
+ intertype: 'alias',
+ ident: toNiceIdent(item.tokens[0].text),
+ value: parseLLVMSegment(item.tokens.slice(3, last)),
+ lineNum: item.lineNum
+ };
+ ret.type = ret.value.type;
+ Types.needAnalysis[ret.type] = 0;
+ if (!NAMED_GLOBALS) {
+ Variables.globals[ret.ident].type = ret.type;
+ }
+ return ret;
+ }
+ if (item.tokens[2].text == 'type') {
+ var fields = [];
+ var packed = false;
+ if (Runtime.isNumberType(item.tokens[3].text)) {
+ // Clang sometimes has |= i32| instead of |= { i32 }|
+ fields = [item.tokens[3].text];
+ } else if (item.tokens[3].text != 'opaque') {
+ if (item.tokens[3].type == '<') {
+ packed = true;
+ item.tokens[3] = item.tokens[3].item.tokens[0];
}
- return [{
- intertype: 'type',
- name_: item.tokens[0].text,
- fields: fields,
- packed: packed,
- lineNum: item.lineNum
- }];
- } else {
- // variable
- var ident = item.tokens[0].text;
- var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0;
- var named = findTokenText(item, 'unnamed_addr') < 0;
- cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]);
- var external = false;
- if (item.tokens[2].text === 'external') {
- external = true;
- item.tokens.splice(2, 1);
+ var subTokens = item.tokens[3].tokens;
+ if (subTokens) {
+ subTokens.push({text:','});
+ while (subTokens[0]) {
+ var stop = 1;
+ while ([','].indexOf(subTokens[stop].text) == -1) stop ++;
+ fields.push(combineTokens(subTokens.slice(0, stop)).text);
+ subTokens.splice(0, stop+1);
+ }
}
- Types.needAnalysis[item.tokens[2].text] = 0;
- var ret = {
- intertype: 'globalVariable',
- ident: toNiceIdent(ident),
- type: item.tokens[2].text,
- external: external,
- private_: private_,
- named: named,
- lineNum: item.lineNum
- };
- if (!NAMED_GLOBALS) {
- Variables.globals[ret.ident].type = ret.type;
- Variables.globals[ret.ident].external = external;
+ }
+ return {
+ intertype: 'type',
+ name_: item.tokens[0].text,
+ fields: fields,
+ packed: packed,
+ lineNum: item.lineNum
+ };
+ } else {
+ // variable
+ var ident = item.tokens[0].text;
+ var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0;
+ var named = findTokenText(item, 'unnamed_addr') < 0;
+ cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]);
+ var external = false;
+ if (item.tokens[2].text === 'external') {
+ external = true;
+ item.tokens.splice(2, 1);
+ }
+ var ret = {
+ intertype: 'globalVariable',
+ ident: toNiceIdent(ident),
+ type: item.tokens[2].text,
+ external: external,
+ private_: private_,
+ named: named,
+ lineNum: item.lineNum
+ };
+ noteGlobalVariable(ret);
+ if (ident == '@llvm.global_ctors') {
+ ret.ctors = [];
+ if (item.tokens[3].item) {
+ var subTokens = item.tokens[3].item.tokens;
+ splitTokenList(subTokens).forEach(function(segment) {
+ var ctor = toNiceIdent(segment[1].tokens.slice(-1)[0].text);
+ ret.ctors.push(ctor);
+ if (ASM_JS) { // must export the global constructors from asm.js module, so mark as implemented and exported
+ Functions.implementedFunctions[ctor] = 'v';
+ EXPORTED_FUNCTIONS[ctor] = 1;
+ }
+ });
}
- Types.needAnalysis[ret.type] = 0;
- if (ident == '@llvm.global_ctors') {
- ret.ctors = [];
- if (item.tokens[3].item) {
- var subTokens = item.tokens[3].item.tokens;
- splitTokenList(subTokens).forEach(function(segment) {
- var ctor = toNiceIdent(segment[1].tokens.slice(-1)[0].text);
- ret.ctors.push(ctor);
- if (ASM_JS) { // must export the global constructors from asm.js module, so mark as implemented and exported
- Functions.implementedFunctions[ctor] = 'v';
- EXPORTED_FUNCTIONS[ctor] = 1;
- }
- });
+ } else if (!external) {
+ if (item.tokens[3] && item.tokens[3].text != ';') {
+ if (item.tokens[3].text == 'c') {
+ item.tokens.splice(3, 1);
}
- } else if (!external) {
- if (item.tokens[3] && item.tokens[3].text != ';') {
- if (item.tokens[3].text == 'c') {
- item.tokens.splice(3, 1);
- }
- if (item.tokens[3].text in PARSABLE_LLVM_FUNCTIONS) {
- ret.value = parseLLVMFunctionCall(item.tokens.slice(2));
- } else {
- ret.value = scanConst(item.tokens[3], ret.type);
- }
+ if (item.tokens[3].text in PARSABLE_LLVM_FUNCTIONS) {
+ ret.value = parseLLVMFunctionCall(item.tokens.slice(2));
} else {
- ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type };
+ ret.value = scanConst(item.tokens[3], ret.type);
}
+ } else {
+ ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type };
}
- return [ret];
}
+ return ret;
}
- });
+ }
// function header
- var funcHeader = substrate.addActor('FuncHeader', {
- processItem: function(item) {
- item.tokens = item.tokens.filter(function(token) {
- return !(token.text in LLVM.LINKAGES || token.text in LLVM.PARAM_ATTR || token.text in LLVM.FUNC_ATTR || token.text in LLVM.CALLING_CONVENTIONS);
- });
- var params = parseParamTokens(item.tokens[2].item.tokens);
- if (sidePass) dprint('unparsedFunctions', 'Processing function: ' + item.tokens[1].text);
- return [{
- intertype: 'function',
- ident: toNiceIdent(item.tokens[1].text),
- returnType: item.tokens[0].text,
- params: params,
- hasVarArgs: hasVarArgs(params),
- lineNum: item.lineNum,
- }];
- }
- });
+ function funcHeaderHandler(item) {
+ item.tokens = item.tokens.filter(function(token) {
+ return !(token.text in LLVM.LINKAGES || token.text in LLVM.PARAM_ATTR || token.text in LLVM.FUNC_ATTR || token.text in LLVM.CALLING_CONVENTIONS);
+ });
+ var params = parseParamTokens(item.tokens[2].item.tokens);
+ if (sidePass) dprint('unparsedFunctions', 'Processing function: ' + item.tokens[1].text);
+ return {
+ intertype: 'function',
+ ident: toNiceIdent(item.tokens[1].text),
+ returnType: item.tokens[0].text,
+ params: params,
+ hasVarArgs: hasVarArgs(params),
+ lineNum: item.lineNum,
+ };
+ }
// label
- substrate.addActor('Label', {
- processItem: function(item) {
- var rawLabel = item.tokens[0].text.substr(-1) == ':' ?
- '%' + item.tokens[0].text.substr(0, item.tokens[0].text.length-1) :
- (item.tokens[1].text == '<label>' ?
- '%' + item.tokens[2].text.substr(1) :
- '%' + item.tokens[0].text)
- var niceLabel = toNiceIdent(rawLabel);
- return [{
- intertype: 'label',
- ident: niceLabel,
- lineNum: item.lineNum
- }];
- }
- });
-
- // TODO: remove dis
- substrate.addActor('Reintegrator', {
- processItem: function(item) {
- this.forwardItem(item, '/dev/stdout');
- }
- });
+ function labelHandler(item) {
+ var rawLabel = item.tokens[0].text.substr(-1) == ':' ?
+ '%' + item.tokens[0].text.substr(0, item.tokens[0].text.length-1) :
+ (item.tokens[1].text == '<label>' ?
+ '%' + item.tokens[2].text.substr(1) :
+ '%' + item.tokens[0].text)
+ var niceLabel = toNiceIdent(rawLabel);
+ return {
+ intertype: 'label',
+ ident: niceLabel,
+ lineNum: item.lineNum
+ };
+ }
// 'load'
- substrate.addActor('Load', {
- processItem: function(item) {
- item.intertype = 'load';
- cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]);
- item.pointerType = item.tokens[1].text;
- item.valueType = item.type = removePointing(item.pointerType);
- Types.needAnalysis[item.type] = 0;
- var last = getTokenIndexByText(item.tokens, ';');
- var segments = splitTokenList(item.tokens.slice(1, last));
- item.pointer = parseLLVMSegment(segments[0]);
- if (segments.length > 1) {
- assert(segments[1][0].text == 'align');
- item.align = parseInt(segments[1][1].text) || QUANTUM_SIZE; // 0 means preferred arch align
- } else {
- item.align = QUANTUM_SIZE;
- }
- item.ident = item.pointer.ident || null;
- this.forwardItem(item, 'Reintegrator');
+ function loadHandler(item) {
+ item.intertype = 'load';
+ cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]);
+ item.pointerType = item.tokens[1].text;
+ item.valueType = item.type = removePointing(item.pointerType);
+ Types.needAnalysis[item.type] = 0;
+ var last = getTokenIndexByText(item.tokens, ';');
+ var segments = splitTokenList(item.tokens.slice(1, last));
+ item.pointer = parseLLVMSegment(segments[0]);
+ if (segments.length > 1) {
+ assert(segments[1][0].text == 'align');
+ item.align = parseInt(segments[1][1].text) || QUANTUM_SIZE; // 0 means preferred arch align
+ } else {
+ item.align = QUANTUM_SIZE;
}
- });
+ item.ident = item.pointer.ident || null;
+ return item;
+ }
// 'extractvalue'
- substrate.addActor('ExtractValue', {
- processItem: function(item) {
- var last = getTokenIndexByText(item.tokens, ';');
- item.intertype = 'extractvalue';
- item.type = item.tokens[1].text; // Of the origin aggregate - not what we extract from it. For that, can only infer it later
- Types.needAnalysis[item.type] = 0;
- item.ident = toNiceIdent(item.tokens[2].text);
- item.indexes = splitTokenList(item.tokens.slice(4, last));
- this.forwardItem(item, 'Reintegrator');
- }
- });
+ function extractValueHandler(item) {
+ var last = getTokenIndexByText(item.tokens, ';');
+ item.intertype = 'extractvalue';
+ item.type = item.tokens[1].text; // Of the origin aggregate - not what we extract from it. For that, can only infer it later
+ Types.needAnalysis[item.type] = 0;
+ item.ident = toNiceIdent(item.tokens[2].text);
+ item.indexes = splitTokenList(item.tokens.slice(4, last));
+ return item;
+ }
// 'insertvalue'
- substrate.addActor('InsertValue', {
- processItem: function(item) {
- var last = getTokenIndexByText(item.tokens, ';');
- item.intertype = 'insertvalue';
- item.type = item.tokens[1].text; // Of the origin aggregate, as well as the result
- Types.needAnalysis[item.type] = 0;
- item.ident = toNiceIdent(item.tokens[2].text);
- var segments = splitTokenList(item.tokens.slice(4, last));
- item.value = parseLLVMSegment(segments[0]);
- item.indexes = segments.slice(1);
- this.forwardItem(item, 'Reintegrator');
- }
- });
+ function insertValueHandler(item) {
+ var last = getTokenIndexByText(item.tokens, ';');
+ item.intertype = 'insertvalue';
+ item.type = item.tokens[1].text; // Of the origin aggregate, as well as the result
+ Types.needAnalysis[item.type] = 0;
+ item.ident = toNiceIdent(item.tokens[2].text);
+ var segments = splitTokenList(item.tokens.slice(4, last));
+ item.value = parseLLVMSegment(segments[0]);
+ item.indexes = segments.slice(1);
+ return item;
+ }
// 'bitcast'
- substrate.addActor('Bitcast', {
- processItem: function(item) {
- item.intertype = 'bitcast';
- item.type = item.tokens[4].text; // The final type
- Types.needAnalysis[item.type] = 0;
- var to = getTokenIndexByText(item.tokens, 'to');
- item.params = [parseLLVMSegment(item.tokens.slice(1, to))];
- item.ident = item.params[0].ident;
- item.type2 = item.tokens[1].text; // The original type
- Types.needAnalysis[item.type2] = 0;
- this.forwardItem(item, 'Reintegrator');
- }
- });
+ function bitcastHandler(item) {
+ item.intertype = 'bitcast';
+ item.type = item.tokens[4].text; // The final type
+ Types.needAnalysis[item.type] = 0;
+ var to = getTokenIndexByText(item.tokens, 'to');
+ item.params = [parseLLVMSegment(item.tokens.slice(1, to))];
+ item.ident = item.params[0].ident;
+ item.type2 = item.tokens[1].text; // The original type
+ Types.needAnalysis[item.type2] = 0;
+ return item;
+ }
// 'getelementptr'
- substrate.addActor('GEP', {
- processItem: function(item) {
- var first = 0;
- while (!isType(item.tokens[first].text)) first++;
- Types.needAnalysis[item.tokens[first].text] = 0;
- var last = getTokenIndexByText(item.tokens, ';');
- var segment = [ item.tokens[first], { text: 'getelementptr' }, null, { item: {
- tokens: item.tokens.slice(first, last)
- } } ];
- var data = parseLLVMFunctionCall(segment);
- item.intertype = 'getelementptr';
- item.type = '*'; // We need type info to determine this - all we know is it's a pointer
- item.params = data.params;
- item.ident = data.ident;
- this.forwardItem(item, 'Reintegrator');
- }
- });
+ function GEPHandler(item) {
+ var first = 0;
+ while (!isType(item.tokens[first].text)) first++;
+ Types.needAnalysis[item.tokens[first].text] = 0;
+ var last = getTokenIndexByText(item.tokens, ';');
+ var segment = [ item.tokens[first], { text: 'getelementptr' }, null, { item: {
+ tokens: item.tokens.slice(first, last)
+ } } ];
+ var data = parseLLVMFunctionCall(segment);
+ item.intertype = 'getelementptr';
+ item.type = '*'; // We need type info to determine this - all we know is it's a pointer
+ item.params = data.params;
+ item.ident = data.ident;
+ return item;
+ }
// 'call', 'invoke'
function makeCall(item, type) {
item.intertype = type;
@@ -727,7 +686,7 @@ function intertyper(data, sidePass, baseLineNums) {
});
if (item.assignTo) item.ident = 'return ' + item.ident;
item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');';
- return { forward: null, ret: [item], item: item };
+ return { forward: null, ret: item, item: item };
}
if (item.ident.substr(-2) == '()') {
// See comment in isStructType()
@@ -750,330 +709,417 @@ function intertyper(data, sidePass, baseLineNums) {
if (item.indent == 2) {
// standalone call - not in assign
item.standalone = true;
- return { forward: null, ret: [item], item: item };
+ return { forward: null, ret: item, item: item };
}
- return { forward: item, ret: [], item: item };
+ return { forward: item, ret: null, item: item };
}
- substrate.addActor('Call', {
- processItem: function(item) {
- var result = makeCall.call(this, item, 'call');
- if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
- return result.ret;
- }
- });
- substrate.addActor('Invoke', {
- processItem: function(item) {
- var result = makeCall.call(this, item, 'invoke');
- if (DISABLE_EXCEPTION_CATCHING == 1) {
- result.item.intertype = 'call';
- result.ret.push({
- intertype: 'branch',
- label: result.item.toLabel,
- lineNum: (result.forward ? item.parentLineNum : item.lineNum) + 0.5
- });
- }
- if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
- return result.ret;
+ function callHandler(item) {
+ var result = makeCall.call(this, item, 'call');
+ if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
+ return result.ret;
+ }
+ function invokeHandler(item) {
+ var result = makeCall.call(this, item, 'invoke');
+ if (DISABLE_EXCEPTION_CATCHING == 1) {
+ result.item.intertype = 'call';
+ finalResults.push({
+ intertype: 'branch',
+ label: result.item.toLabel,
+ lineNum: (result.forward ? item.parentLineNum : item.lineNum) + 0.5
+ });
}
- });
- substrate.addActor('Atomic', {
- processItem: function(item) {
- item.intertype = 'atomic';
- if (item.tokens[0].text == 'atomicrmw') {
- if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
- item.op = item.tokens[1].text;
- item.tokens.splice(1, 1);
- } else {
- assert(item.tokens[0].text == 'cmpxchg')
- if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
- item.op = 'cmpxchg';
- }
- var last = getTokenIndexByText(item.tokens, ';');
- item.params = splitTokenList(item.tokens.slice(1, last)).map(parseLLVMSegment);
- item.type = item.params[1].type;
- this.forwardItem(item, 'Reintegrator');
+ if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
+ return result.ret;
+ }
+ function atomicHandler(item) {
+ item.intertype = 'atomic';
+ if (item.tokens[0].text == 'atomicrmw') {
+ if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
+ item.op = item.tokens[1].text;
+ item.tokens.splice(1, 1);
+ } else {
+ assert(item.tokens[0].text == 'cmpxchg')
+ if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
+ item.op = 'cmpxchg';
}
- });
+ var last = getTokenIndexByText(item.tokens, ';');
+ item.params = splitTokenList(item.tokens.slice(1, last)).map(parseLLVMSegment);
+ item.type = item.params[1].type;
+ return item;
+ }
// 'landingpad'
- substrate.addActor('Landingpad', {
- processItem: function(item) {
- item.intertype = 'landingpad';
- item.type = item.tokens[1].text;
- item.catchables = [];
- var catchIdx = findTokenText(item, "catch");
- if (catchIdx != -1) {
- do {
- var nextCatchIdx = findTokenTextAfter(item, "catch", catchIdx+1);
- if (nextCatchIdx == -1)
- nextCatchIdx = item.tokens.length;
- item.catchables.push(parseLLVMSegment(item.tokens.slice(catchIdx+2, nextCatchIdx)));
- catchIdx = nextCatchIdx;
- } while (catchIdx != item.tokens.length);
- }
- Types.needAnalysis[item.type] = 0;
- this.forwardItem(item, 'Reintegrator');
+ function landingpadHandler(item) {
+ item.intertype = 'landingpad';
+ item.type = item.tokens[1].text;
+ item.catchables = [];
+ var catchIdx = findTokenText(item, "catch");
+ if (catchIdx != -1) {
+ do {
+ var nextCatchIdx = findTokenTextAfter(item, "catch", catchIdx+1);
+ if (nextCatchIdx == -1)
+ nextCatchIdx = item.tokens.length;
+ item.catchables.push(parseLLVMSegment(item.tokens.slice(catchIdx+2, nextCatchIdx)));
+ catchIdx = nextCatchIdx;
+ } while (catchIdx != item.tokens.length);
}
- });
+ Types.needAnalysis[item.type] = 0;
+ return item;
+ }
// 'alloca'
var allocaPossibleVars = ['allocatedNum'];
- substrate.addActor('Alloca', {
- processItem: function(item) {
- item.intertype = 'alloca';
- item.allocatedType = item.tokens[1].text;
- if (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) {
- item.allocatedNum = toNiceIdent(item.tokens[4].text);
- item.possibleVars = allocaPossibleVars;
- } else {
- item.allocatedNum = 1;
- }
- item.type = addPointing(item.tokens[1].text); // type of pointer we will get
- Types.needAnalysis[item.type] = 0;
- item.type2 = item.tokens[1].text; // value we will create, and get a pointer to
- Types.needAnalysis[item.type2] = 0;
- this.forwardItem(item, 'Reintegrator');
+ function allocaHandler(item) {
+ item.intertype = 'alloca';
+ item.allocatedType = item.tokens[1].text;
+ if (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) {
+ item.allocatedNum = toNiceIdent(item.tokens[4].text);
+ item.possibleVars = allocaPossibleVars;
+ } else {
+ item.allocatedNum = 1;
}
- });
+ item.type = addPointing(item.tokens[1].text); // type of pointer we will get
+ Types.needAnalysis[item.type] = 0;
+ item.type2 = item.tokens[1].text; // value we will create, and get a pointer to
+ Types.needAnalysis[item.type2] = 0;
+ return item;
+ }
// 'phi'
- substrate.addActor('Phi', {
- processItem: function(item) {
- item.intertype = 'phi';
- item.type = item.tokens[1].text;
- var typeToken = [item.tokens[1]];
- Types.needAnalysis[item.type] = 0;
- var last = getTokenIndexByText(item.tokens, ';');
- item.params = splitTokenList(item.tokens.slice(2, last)).map(function(segment) {
- var subSegments = splitTokenList(segment[0].item.tokens);
- var ret = {
- intertype: 'phiparam',
- label: toNiceIdent(subSegments[1][0].text),
- value: parseLLVMSegment(typeToken.concat(subSegments[0]))
- };
- return ret;
- }).filter(function(param) { return param.value && param.value.ident != 'undef' });
- this.forwardItem(item, 'Reintegrator');
- }
- });
+ function phiHandler(item) {
+ item.intertype = 'phi';
+ item.type = item.tokens[1].text;
+ var typeToken = [item.tokens[1]];
+ Types.needAnalysis[item.type] = 0;
+ var last = getTokenIndexByText(item.tokens, ';');
+ item.params = splitTokenList(item.tokens.slice(2, last)).map(function(segment) {
+ var subSegments = splitTokenList(segment[0].item.tokens);
+ var ret = {
+ intertype: 'phiparam',
+ label: toNiceIdent(subSegments[1][0].text),
+ value: parseLLVMSegment(typeToken.concat(subSegments[0]))
+ };
+ return ret;
+ }).filter(function(param) { return param.value && param.value.ident != 'undef' });
+ return item;
+ }
// 'phi'
- substrate.addActor('va_arg', {
- processItem: function(item) {
- item.intertype = 'va_arg';
- var segments = splitTokenList(item.tokens.slice(1));
- item.type = segments[1][0].text;
- item.value = parseLLVMSegment(segments[0]);
- this.forwardItem(item, 'Reintegrator');
- }
- });
+ function va_argHandler(item) {
+ item.intertype = 'va_arg';
+ var segments = splitTokenList(item.tokens.slice(1));
+ item.type = segments[1][0].text;
+ item.value = parseLLVMSegment(segments[0]);
+ return item;
+ }
// mathops
- substrate.addActor('Mathops', {
- processItem: function(item) {
- item.intertype = 'mathop';
- item.op = item.tokens[0].text;
- item.variant = null;
- while (item.tokens[1].text in NSW_NUW) item.tokens.splice(1, 1);
- if (['icmp', 'fcmp'].indexOf(item.op) != -1) {
- item.variant = item.tokens[1].text;
- item.tokens.splice(1, 1);
- }
- if (item.tokens[1].text == 'exact') item.tokens.splice(1, 1); // TODO: Implement trap values
- var segments = splitTokenList(item.tokens.slice(1));
- item.params = [];
- for (var i = 1; i <= 4; i++) {
- if (segments[i-1]) {
- if (i > 1 && segments[i-1].length == 1 && segments[0].length > 1 && !isType(segments[i-1][0].text)) {
- segments[i-1].unshift(segments[0][0]); // Add the type from the first segment, they are all alike
- }
- item.params[i-1] = parseLLVMSegment(segments[i-1]);
- }
- }
- var setParamTypes = true;
- if (item.op === 'select') {
- assert(item.params[1].type === item.params[2].type);
- item.type = item.params[1].type;
- } else if (item.op in LLVM.CONVERSIONS) {
- item.type = item.params[1].type;
- setParamTypes = false;
- } else {
- item.type = item.params[0].type;
- }
- if (setParamTypes) {
- for (var i = 0; i < 4; i++) {
- if (item.params[i]) item.params[i].type = item.type; // All params have the same type, normally
+ function mathopsHandler(item) {
+ item.intertype = 'mathop';
+ item.op = item.tokens[0].text;
+ item.variant = null;
+ while (item.tokens[1].text in NSW_NUW) item.tokens.splice(1, 1);
+ if (['icmp', 'fcmp'].indexOf(item.op) != -1) {
+ item.variant = item.tokens[1].text;
+ item.tokens.splice(1, 1);
+ }
+ if (item.tokens[1].text == 'exact') item.tokens.splice(1, 1); // TODO: Implement trap values
+ var segments = splitTokenList(item.tokens.slice(1));
+ item.params = [];
+ for (var i = 1; i <= 4; i++) {
+ if (segments[i-1]) {
+ if (i > 1 && segments[i-1].length == 1 && segments[0].length > 1 && !isType(segments[i-1][0].text)) {
+ segments[i-1].unshift(segments[0][0]); // Add the type from the first segment, they are all alike
}
+ item.params[i-1] = parseLLVMSegment(segments[i-1]);
}
- if (item.op in LLVM.EXTENDS) {
- item.type = item.params[1].ident;
- item.params[0].type = item.params[1].type;
- // TODO: also remove 2nd param?
- } else if (item.op in LLVM.COMPS) {
- item.type = 'i1';
+ }
+ var setParamTypes = true;
+ if (item.op === 'select') {
+ assert(item.params[1].type === item.params[2].type);
+ item.type = item.params[1].type;
+ } else if (item.op in LLVM.CONVERSIONS) {
+ item.type = item.params[1].type;
+ setParamTypes = false;
+ } else {
+ item.type = item.params[0].type;
+ }
+ if (setParamTypes) {
+ for (var i = 0; i < 4; i++) {
+ if (item.params[i]) item.params[i].type = item.type; // All params have the same type, normally
}
- if (USE_TYPED_ARRAYS == 2) {
- // Some specific corrections, since 'i64' is special
- if (item.op in LLVM.SHIFTS) {
- item.params[1].type = 'i32';
- } else if (item.op == 'select') {
- item.params[0].type = 'i1';
- }
+ }
+ if (item.op in LLVM.EXTENDS) {
+ item.type = item.params[1].ident;
+ item.params[0].type = item.params[1].type;
+ // TODO: also remove 2nd param?
+ } else if (item.op in LLVM.COMPS) {
+ item.type = 'i1';
+ }
+ if (USE_TYPED_ARRAYS == 2) {
+ // Some specific corrections, since 'i64' is special
+ if (item.op in LLVM.SHIFTS) {
+ item.params[1].type = 'i32';
+ } else if (item.op == 'select') {
+ item.params[0].type = 'i1';
}
- Types.needAnalysis[item.type] = 0;
- this.forwardItem(item, 'Reintegrator');
}
- });
+ Types.needAnalysis[item.type] = 0;
+ return item;
+ }
// 'store'
- substrate.addActor('Store', {
- processItem: function(item) {
- cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]);
- var segments = splitTokenList(item.tokens.slice(1));
- var ret = {
- intertype: 'store',
- valueType: item.tokens[1].text,
- value: parseLLVMSegment(segments[0]),
- pointer: parseLLVMSegment(segments[1]),
- lineNum: item.lineNum
- };
- Types.needAnalysis[ret.valueType] = 0;
- ret.ident = toNiceIdent(ret.pointer.ident);
- ret.pointerType = ret.pointer.type;
- Types.needAnalysis[ret.pointerType] = 0;
- if (segments.length > 2) {
- assert(segments[2][0].text == 'align');
- ret.align = parseInt(segments[2][1].text) || QUANTUM_SIZE; // 0 means preferred arch align
- } else {
- ret.align = QUANTUM_SIZE;
- }
- return [ret];
+ function storeHandler(item) {
+ cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]);
+ var segments = splitTokenList(item.tokens.slice(1));
+ var ret = {
+ intertype: 'store',
+ valueType: item.tokens[1].text,
+ value: parseLLVMSegment(segments[0]),
+ pointer: parseLLVMSegment(segments[1]),
+ lineNum: item.lineNum
+ };
+ Types.needAnalysis[ret.valueType] = 0;
+ ret.ident = toNiceIdent(ret.pointer.ident);
+ ret.pointerType = ret.pointer.type;
+ Types.needAnalysis[ret.pointerType] = 0;
+ if (segments.length > 2) {
+ assert(segments[2][0].text == 'align');
+ ret.align = parseInt(segments[2][1].text) || QUANTUM_SIZE; // 0 means preferred arch align
+ } else {
+ ret.align = QUANTUM_SIZE;
}
- });
+ return ret;
+ }
// 'br'
- substrate.addActor('Branch', {
- processItem: function(item) {
- if (item.tokens[1].text == 'label') {
- return [{
- intertype: 'branch',
- label: toNiceIdent(item.tokens[2].text),
- lineNum: item.lineNum
- }];
- } else {
- var commaIndex = findTokenText(item, ',');
- return [{
- intertype: 'branch',
- value: parseLLVMSegment(item.tokens.slice(1, commaIndex)),
- labelTrue: toNiceIdent(item.tokens[commaIndex+2].text),
- labelFalse: toNiceIdent(item.tokens[commaIndex+5].text),
- lineNum: item.lineNum
- }];
- }
- }
- });
- // 'ret'
- substrate.addActor('Return', {
- processItem: function(item) {
- var type = item.tokens[1].text;
- Types.needAnalysis[type] = 0;
- return [{
- intertype: 'return',
- type: type,
- value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(1)) : null,
+ function branchHandler(item) {
+ if (item.tokens[1].text == 'label') {
+ return {
+ intertype: 'branch',
+ label: toNiceIdent(item.tokens[2].text),
lineNum: item.lineNum
- }];
- }
- });
- // 'resume' - partial implementation
- substrate.addActor('Resume', {
- processItem: function(item) {
- return [{
- intertype: 'resume',
- ident: toNiceIdent(item.tokens[2].text),
+ };
+ } else {
+ var commaIndex = findTokenText(item, ',');
+ return {
+ intertype: 'branch',
+ value: parseLLVMSegment(item.tokens.slice(1, commaIndex)),
+ labelTrue: toNiceIdent(item.tokens[commaIndex+2].text),
+ labelFalse: toNiceIdent(item.tokens[commaIndex+5].text),
lineNum: item.lineNum
- }];
+ };
}
- });
+ }
+ // 'ret'
+ function returnHandler(item) {
+ var type = item.tokens[1].text;
+ Types.needAnalysis[type] = 0;
+ return {
+ intertype: 'return',
+ type: type,
+ value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(1)) : null,
+ lineNum: item.lineNum
+ };
+ }
+ // 'resume' - partial implementation
+ function resumeHandler(item) {
+ return {
+ intertype: 'resume',
+ ident: toNiceIdent(item.tokens[2].text),
+ lineNum: item.lineNum
+ };
+ }
// 'switch'
- substrate.addActor('Switch', {
- processItem: function(item) {
- function parseSwitchLabels(item) {
- var ret = [];
- var tokens = item.item.tokens;
- while (tokens.length > 0) {
- ret.push({
- value: tokens[1].text,
- label: toNiceIdent(tokens[4].text)
- });
- tokens = tokens.slice(5);
- }
- return ret;
+ function switchHandler(item) {
+ function parseSwitchLabels(item) {
+ var ret = [];
+ var tokens = item.item.tokens;
+ while (tokens.length > 0) {
+ ret.push({
+ value: tokens[1].text,
+ label: toNiceIdent(tokens[4].text)
+ });
+ tokens = tokens.slice(5);
}
- var type = item.tokens[1].text;
- Types.needAnalysis[type] = 0;
- return [{
- intertype: 'switch',
- type: type,
- ident: toNiceIdent(item.tokens[2].text),
- defaultLabel: toNiceIdent(item.tokens[5].text),
- switchLabels: parseSwitchLabels(item.tokens[6]),
- lineNum: item.lineNum
- }];
+ return ret;
}
- });
+ var type = item.tokens[1].text;
+ Types.needAnalysis[type] = 0;
+ return {
+ intertype: 'switch',
+ type: type,
+ ident: toNiceIdent(item.tokens[2].text),
+ defaultLabel: toNiceIdent(item.tokens[5].text),
+ switchLabels: parseSwitchLabels(item.tokens[6]),
+ lineNum: item.lineNum
+ };
+ }
// function end
- substrate.addActor('FuncEnd', {
- processItem: function(item) {
- return [{
- intertype: 'functionEnd',
- lineNum: item.lineNum
- }];
- }
- });
+ function funcEndHandler(item) {
+ return {
+ intertype: 'functionEnd',
+ lineNum: item.lineNum
+ };
+ }
// external function stub
- substrate.addActor('External', {
- processItem: function(item) {
- while (item.tokens[1].text in LLVM.LINKAGES || item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.VISIBILITIES || item.tokens[1].text in LLVM.CALLING_CONVENTIONS) {
- item.tokens.splice(1, 1);
- }
- var params = parseParamTokens(item.tokens[3].item.tokens);
- return [{
- intertype: 'functionStub',
- ident: toNiceIdent(item.tokens[2].text),
- returnType: item.tokens[1],
- params: params,
- hasVarArgs: hasVarArgs(params),
- lineNum: item.lineNum
- }];
+ function externalHandler(item) {
+ while (item.tokens[1].text in LLVM.LINKAGES || item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.VISIBILITIES || item.tokens[1].text in LLVM.CALLING_CONVENTIONS) {
+ item.tokens.splice(1, 1);
}
- });
+ var params = parseParamTokens(item.tokens[3].item.tokens);
+ return {
+ intertype: 'functionStub',
+ ident: toNiceIdent(item.tokens[2].text),
+ returnType: item.tokens[1],
+ params: params,
+ hasVarArgs: hasVarArgs(params),
+ lineNum: item.lineNum
+ };
+ }
// 'unreachable'
- substrate.addActor('Unreachable', {
- processItem: function(item) {
- return [{
- intertype: 'unreachable',
- lineNum: item.lineNum
- }];
- }
- });
+ function unreachableHandler(item) {
+ return {
+ intertype: 'unreachable',
+ lineNum: item.lineNum
+ };
+ }
// 'indirectbr'
- substrate.addActor('IndirectBr', {
- processItem: function(item) {
- var ret = {
- intertype: 'indirectbr',
- value: parseLLVMSegment(splitTokenList(item.tokens.slice(1))[0]),
- type: item.tokens[1].text,
- lineNum: item.lineNum
- };
- Types.needAnalysis[ret.type] = 0;
- return [ret];
+ function indirectBrHandler(item) {
+ var ret = {
+ intertype: 'indirectbr',
+ value: parseLLVMSegment(splitTokenList(item.tokens.slice(1))[0]),
+ type: item.tokens[1].text,
+ lineNum: item.lineNum
+ };
+ Types.needAnalysis[ret.type] = 0;
+ return ret;
+ }
+
+ // Fast paths - quick parses of common patterns, avoid tokenizing entirely
+
+ function tryFastPaths(line) {
+ var m, ret;
+ if (phase === 'pre') {
+ // string constant
+ if (0) { // works, but not worth it m = /([@\.\w\d_]+) = (private )?(unnamed_addr )?(constant )?(\[\d+ x i8\]) c"([^"]+)".*/.exec(line.lineText)) {
+ if (m[1] === '@llvm.global_ctors') return ret;
+ ret = {
+ intertype: 'globalVariable',
+ ident: toNiceIdent(m[1]),
+ type: m[5],
+ external: false,
+ private_: m[2] !== null,
+ named: m[3] === null,
+ lineNum: line.lineNum,
+ value: {
+ intertype: 'string',
+ text: m[6]
+ }
+ };
+ noteGlobalVariable(ret);
+ }
+ } else if (phase === 'funcs') {
+ if (m = /^ (%[\w\d\._]+) = (getelementptr|load) ([%\w\d\._ ,\*\-@]+)$/.exec(line.lineText)) {
+ var assignTo = m[1];
+ var intertype = m[2];
+ var args = m[3];
+ switch (intertype) {
+ case 'getelementptr': {
+ if (args[0] === 'i' && args.indexOf('inbounds ') === 0) {
+ args = args.substr(9);
+ }
+ var params = args.split(', ').map(function(param) {
+ var parts = param.split(' ');
+ assert(parts.length === 2);
+ Types.needAnalysis[parts[0]] = 0;
+ return {
+ intertype: 'value',
+ type: parts[0],
+ ident: toNiceIdent(parts[1]),
+ byVal: 0
+ }
+ });
+ ret = {
+ intertype: 'getelementptr',
+ lineNum: line.lineNum,
+ assignTo: toNiceIdent(assignTo),
+ ident: params[0].ident,
+ type: '*',
+ params: params
+ };
+ break;
+ }
+ case 'load': {
+ if (m = /(^[%\w\d\._\-@\*]+) ([%\w\d\._\-@]+)(, align \d+)?$/.exec(args)) {
+ var ident = toNiceIdent(m[2]);
+ var type = m[1];
+ assert(type[type.length-1] === '*', type);
+ var valueType = type.substr(0, type.length-1);
+ ret = {
+ intertype: 'load',
+ lineNum: line.lineNum,
+ assignTo: toNiceIdent(assignTo),
+ ident: ident,
+ type: valueType,
+ valueType: valueType,
+ pointerType: type,
+ pointer: {
+ intertype: 'value',
+ ident: ident,
+ type: type,
+ },
+ align: parseAlign(m[3])
+ };
+ }
+ break;
+ }
+ default: throw 'unexpected fast path type ' + intertype;
+ }
+ //else if (line.lineText.indexOf(' = load ') > 0) printErr('close: ' + JSON.stringify(line.lineText));
+ }
}
- });
+ if (ret) {
+ if (COMPILER_ASSERTIONS) {
+ //printErr(['\n', JSON.stringify(ret), '\n', JSON.stringify(triager(tokenizer(line)))]);
+ var normal = triager(tokenizer(line));
+ delete normal.tokens;
+ delete normal.indent;
+ assert(sortedJsonCompare(normal, ret), 'fast path: ' + dump(normal) + '\n vs \n' + dump(ret));
+ }
+ }
+ return ret;
+ }
// Input
- substrate.addItem({
- llvmLines: data
- }, 'LineSplitter');
+ lineSplitter().forEach(function(line) {
+ var item = tryFastPaths(line);
+ if (item) {
+ finalResults.push(item);
+ fastPaths++;
+ return;
+ }
+ slowPaths++;
- substrate.onResult = function(result) {
- if (result.tokens) result.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here.
- };
+ //var time = Date.now();
+
+ var t = tokenizer(line);
+ item = triager(t);
+
+ /*
+ var type = item ? item.intertype + (item.op ? ':' + item.op : ''): 'none';
+ if (!interProf[type]) interProf[type] = { ms: 0, n: 0 };
+ interProf[type].ms += Date.now() - time;
+ interProf[type].n++;
+ */
+
+ if (!item) return;
+ finalResults.push(item);
+ if (item.tokens) item.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here.
+ });
+ return finalResults;
+}
+
+// intertyper profiler
- return substrate.solve();
+/*
+var interProf = {};
+function dumpInterProf() {
+ printErr('\nintertyper/' + phase + ' (ms | n): ' + JSON.stringify(keys(interProf).sort(function(x, y) { return interProf[y].ms - interProf[x].ms }).map(function(x) { return x + ' : ' + interProf[x].ms + ' | ' + interProf[x].n }), null, ' ') + '\n');
}
+*/
diff --git a/src/jsifier.js b/src/jsifier.js
index 1f53b1a2..96cb8d9a 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -22,6 +22,8 @@ var functionStubSigs = {};
function JSify(data, functionsOnly, givenFunctions) {
var mainPass = !functionsOnly;
+ var itemsDict = { type: [], GlobalVariableStub: [], functionStub: [], function: [], GlobalVariable: [], GlobalVariablePostSet: [] };
+
if (mainPass) {
var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js');
@@ -58,17 +60,6 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
- // Does simple 'macro' substitution, using Django-like syntax,
- // {{{ code }}} will be replaced with |eval(code)|.
- function processMacros(text) {
- return text.replace(/{{{[^}]+}}}/g, function(str) {
- str = str.substr(3, str.length-6);
- return eval(str).toString();
- });
- }
-
- var substrate = new Substrate('JSifyer');
-
if (mainPass) {
// Handle unparsed types TODO: Batch them
analyzer(intertyper(data.unparsedTypess[0].lines, true), true);
@@ -138,21 +129,6 @@ function JSify(data, functionsOnly, givenFunctions) {
// Actors
- // type
- // FIXME: This is no longer used, we do not actually need to JSify on types. TODO: Remove this and related code
- substrate.addActor('Type', {
- processItem: function(item) {
- var type = Types.types[item.name_];
- var niceName = toNiceIdent(item.name_);
- // We might export all of Types.types, cleaner that way, but do not want slowdowns in accessing flatteners
- item.JS = 'var ' + niceName + '___SIZE = ' + Types.types[item.name_].flatSize + '; // ' + item.name_ + '\n';
- if (type.needsFlattening && !type.flatFactor) {
- item.JS += 'var ' + niceName + '___FLATTENER = ' + JSON.stringify(Types.types[item.name_].flatIndexes) + ';';
- }
- return [item];
- }
- });
-
function makeEmptyStruct(type) {
var ret = [];
var typeData = Types.types[type];
@@ -255,146 +231,144 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// globalVariable
- substrate.addActor('GlobalVariable', {
- processItem: function(item) {
- function needsPostSet(value) {
- if (typeof value !== 'string') return false;
- return value[0] in UNDERSCORE_OPENPARENS || value.substr(0, 14) === 'CHECK_OVERFLOW'
- || value.substr(0, 6) === 'GLOBAL';
- }
-
- item.intertype = 'GlobalVariableStub';
- assert(!item.lines); // FIXME remove this, after we are sure it isn't needed
- var ret = [item];
- if (item.ident == '_llvm_global_ctors') {
- item.JS = '\n/* global initializers */ __ATINIT__.push(' +
- item.ctors.map(function(ctor) { return '{ func: function() { ' + ctor + '() } }' }).join(',') +
- ');\n';
- return ret;
- }
+ function globalVariableHandler(item) {
+ function needsPostSet(value) {
+ if (typeof value !== 'string') return false;
+ return value[0] in UNDERSCORE_OPENPARENS || value.substr(0, 14) === 'CHECK_OVERFLOW'
+ || value.substr(0, 6) === 'GLOBAL';
+ }
+
+ item.intertype = 'GlobalVariableStub';
+ itemsDict.GlobalVariableStub.push(item);
+ assert(!item.lines); // FIXME remove this, after we are sure it isn't needed
+ if (item.ident == '_llvm_global_ctors') {
+ item.JS = '\n/* global initializers */ __ATINIT__.push(' +
+ item.ctors.map(function(ctor) { return '{ func: function() { ' + ctor + '() } }' }).join(',') +
+ ');\n';
+ return;
+ }
- var constant = null;
- var allocator = (BUILD_AS_SHARED_LIB && !item.external) ? 'ALLOC_NORMAL' : 'ALLOC_STATIC';
- var index = null;
- if (item.external && BUILD_AS_SHARED_LIB) {
- // External variables in shared libraries should not be declared as
- // they would shadow similarly-named globals in the parent.
- item.JS = '';
- } else {
- item.JS = makeGlobalDef(item.ident);
- }
+ var constant = null;
+ var allocator = (BUILD_AS_SHARED_LIB && !item.external) ? 'ALLOC_NORMAL' : 'ALLOC_STATIC';
+ var index = null;
+ if (item.external && BUILD_AS_SHARED_LIB) {
+ // External variables in shared libraries should not be declared as
+ // they would shadow similarly-named globals in the parent.
+ item.JS = '';
+ } else {
+ item.JS = makeGlobalDef(item.ident);
+ }
- if (!NAMED_GLOBALS && isIndexableGlobal(item.ident)) {
- index = makeGlobalUse(item.ident); // index !== null indicates we are indexing this
- allocator = 'ALLOC_NONE';
- }
+ if (!NAMED_GLOBALS && isIndexableGlobal(item.ident)) {
+ index = makeGlobalUse(item.ident); // index !== null indicates we are indexing this
+ allocator = 'ALLOC_NONE';
+ }
- Variables.globals[item.ident].named = item.named;
+ Variables.globals[item.ident].named = item.named;
- if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) {
- // We need this to be named (and it normally would not be), so that it can be linked to and used from other modules
- Variables.globals[item.ident].linkable = 1;
- }
+ if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) {
+ // We need this to be named (and it normally would not be), so that it can be linked to and used from other modules
+ Variables.globals[item.ident].linkable = 1;
+ }
- if (isBSS(item)) {
- var length = calcAllocatedSize(item.type);
- length = Runtime.alignMemory(length);
+ if (isBSS(item)) {
+ var length = calcAllocatedSize(item.type);
+ length = Runtime.alignMemory(length);
- // If using indexed globals, go ahead and early out (no need to explicitly
- // initialize).
- if (!NAMED_GLOBALS) {
- return ret;
- }
- // If using named globals, we can at least shorten the call to allocate by
- // passing an integer representing the size of memory to alloc instead of
- // an array of 0s of size length.
- else {
- constant = length;
+ // If using indexed globals, go ahead and early out (no need to explicitly
+ // initialize).
+ if (!NAMED_GLOBALS) {
+ return;
+ }
+ // If using named globals, we can at least shorten the call to allocate by
+ // passing an integer representing the size of memory to alloc instead of
+ // an array of 0s of size length.
+ else {
+ constant = length;
+ }
+ } else {
+ if (item.external) {
+ if (Runtime.isNumberType(item.type) || isPointerType(item.type)) {
+ constant = zeros(Runtime.getNativeFieldSize(item.type));
+ } else {
+ constant = makeEmptyStruct(item.type);
}
} else {
- if (item.external) {
- if (Runtime.isNumberType(item.type) || isPointerType(item.type)) {
- constant = zeros(Runtime.getNativeFieldSize(item.type));
- } else {
- constant = makeEmptyStruct(item.type);
- }
- } else {
- constant = parseConst(item.value, item.type, item.ident);
+ constant = parseConst(item.value, item.type, item.ident);
+ }
+ assert(typeof constant === 'object');//, [typeof constant, JSON.stringify(constant), item.external]);
+
+ // This is a flattened object. We need to find its idents, so they can be assigned to later
+ var structTypes = null;
+ constant.forEach(function(value, i) {
+ if (needsPostSet(value)) { // ident, or expression containing an ident
+ if (!structTypes) structTypes = generateStructTypes(item.type);
+ itemsDict.GlobalVariablePostSet.push({
+ intertype: 'GlobalVariablePostSet',
+ JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors
+ });
+ constant[i] = '0';
}
- assert(typeof constant === 'object');//, [typeof constant, JSON.stringify(constant), item.external]);
-
- // This is a flattened object. We need to find its idents, so they can be assigned to later
- var structTypes = null;
- constant.forEach(function(value, i) {
- if (needsPostSet(value)) { // ident, or expression containing an ident
- if (!structTypes) structTypes = generateStructTypes(item.type);
- ret.push({
- intertype: 'GlobalVariablePostSet',
- JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors
- });
- constant[i] = '0';
- }
- });
+ });
- if (item.external) {
- // External variables in shared libraries should not be declared as
- // they would shadow similarly-named globals in the parent, so do nothing here.
- if (BUILD_AS_SHARED_LIB) return ret;
- if (SIDE_MODULE) return [];
- // Library items need us to emit something, but everything else requires nothing.
- if (!LibraryManager.library[item.ident.slice(1)]) return ret;
+ if (item.external) {
+ // External variables in shared libraries should not be declared as
+ // they would shadow similarly-named globals in the parent, so do nothing here.
+ if (BUILD_AS_SHARED_LIB) return;
+ if (SIDE_MODULE) {
+ itemsDict.GlobalVariableStub.pop(); // remove this item
+ return;
}
+ // Library items need us to emit something, but everything else requires nothing.
+ if (!LibraryManager.library[item.ident.slice(1)]) return;
+ }
- // ensure alignment
- constant = constant.concat(zeros(Runtime.alignMemory(constant.length) - constant.length));
+ // ensure alignment
+ constant = constant.concat(zeros(Runtime.alignMemory(constant.length) - constant.length));
- // Special case: class vtables. We make sure they are null-terminated, to allow easy runtime operations
- if (item.ident.substr(0, 5) == '__ZTV') {
- constant = constant.concat(zeros(Runtime.alignMemory(QUANTUM_SIZE)));
- }
+ // Special case: class vtables. We make sure they are null-terminated, to allow easy runtime operations
+ if (item.ident.substr(0, 5) == '__ZTV') {
+ constant = constant.concat(zeros(Runtime.alignMemory(QUANTUM_SIZE)));
}
+ }
- // NOTE: This is the only place that could potentially create static
- // allocations in a shared library.
- constant = makePointer(constant, null, allocator, item.type, index);
+ // NOTE: This is the only place that could potentially create static
+ // allocations in a shared library.
+ constant = makePointer(constant, null, allocator, item.type, index);
- var js = (index !== null ? '' : item.ident + '=') + constant;
- if (js) js += ';';
+ var js = (index !== null ? '' : item.ident + '=') + constant;
+ if (js) js += ';';
- if (!ASM_JS && NAMED_GLOBALS && (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS))) {
- js += '\nModule["' + item.ident + '"] = ' + item.ident + ';';
- }
- if (BUILD_AS_SHARED_LIB == 2 && !item.private_) {
- // TODO: make the assert conditional on ASSERTIONS
- js += 'if (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + item.ident + ' }';
- }
- if (item.external && !NAMED_GLOBALS) {
- js = 'var ' + item.ident + ' = ' + js; // force an explicit naming, even if unnamed globals, for asm forwarding
- }
- return ret.concat({
- intertype: 'GlobalVariable',
- JS: js,
- });
+ if (!ASM_JS && NAMED_GLOBALS && (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS))) {
+ js += '\nModule["' + item.ident + '"] = ' + item.ident + ';';
}
- });
+ if (BUILD_AS_SHARED_LIB == 2 && !item.private_) {
+ // TODO: make the assert conditional on ASSERTIONS
+ js += 'if (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + item.ident + ' }';
+ }
+ if (item.external && !NAMED_GLOBALS) {
+ js = 'var ' + item.ident + ' = ' + js; // force an explicit naming, even if unnamed globals, for asm forwarding
+ }
+ itemsDict.GlobalVariableStub.push({
+ intertype: 'GlobalVariable',
+ JS: js,
+ });
+ }
// alias
- substrate.addActor('Alias', {
- processItem: function(item) {
- item.intertype = 'GlobalVariableStub';
- var ret = [item];
- item.JS = 'var ' + item.ident + ';';
- // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there
- Variables.globals[item.ident].targetIdent = item.value.ident;
- var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value);
- if ((MAIN_MODULE || SIDE_MODULE) && isFunctionType(item.type)) {
- var target = item.value.ident;
- if (!Functions.aliases[target]) Functions.aliases[target] = [];
- Functions.aliases[target].push(item.ident);
- }
- return ret;
+ function aliasHandler(item) {
+ item.intertype = 'GlobalVariableStub';
+ itemsDict.GlobalVariableStub.push(item);
+ item.JS = 'var ' + item.ident + ';';
+ // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there
+ Variables.globals[item.ident].targetIdent = item.value.ident;
+ var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value);
+ if ((MAIN_MODULE || SIDE_MODULE) && isFunctionType(item.type)) {
+ var target = item.value.ident;
+ if (!Functions.aliases[target]) Functions.aliases[target] = [];
+ Functions.aliases[target].push(item.ident);
}
- });
+ }
function processLibraryFunction(snippet, ident) {
snippet = snippet.toString();
@@ -411,147 +385,163 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// functionStub
- substrate.addActor('FunctionStub', {
- processItem: function(item) {
- // note the signature
- if (item.returnType && item.params) {
- functionStubSigs[item.ident] = Functions.getSignature(item.returnType.text, item.params.map(function(arg) { return arg.type }), false);
- }
-
- function addFromLibrary(ident) {
- if (ident in addedLibraryItems) return '';
- addedLibraryItems[ident] = true;
-
- // dependencies can be JS functions, which we just run
- if (typeof ident == 'function') return ident();
-
- // Don't replace implemented functions with library ones (which can happen when we add dependencies).
- // Note: We don't return the dependencies here. Be careful not to end up where this matters
- if (('_' + ident) in Functions.implementedFunctions) return '';
-
- var snippet = LibraryManager.library[ident];
- var redirectedIdent = null;
- var deps = LibraryManager.library[ident + '__deps'] || [];
- var isFunction = false;
-
- if (typeof snippet === 'string') {
- var target = LibraryManager.library[snippet];
- if (target) {
- // Redirection for aliases. We include the parent, and at runtime make ourselves equal to it.
- // This avoid having duplicate functions with identical content.
- redirectedIdent = snippet;
- deps.push(snippet);
- snippet = '_' + snippet;
- }
- // In asm, we need to know about library functions. If there is a target, though, then no
- // need to consider this a library function - we will call directly to it anyhow
- if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math\.\w+/.exec(snippet))) {
- Functions.libraryFunctions[ident] = 1;
- }
- } else if (typeof snippet === 'object') {
- snippet = stringifyWithFunctions(snippet);
- } else if (typeof snippet === 'function') {
- isFunction = true;
- snippet = processLibraryFunction(snippet, ident);
- if (ASM_JS) Functions.libraryFunctions[ident] = 1;
+ function functionStubHandler(item) {
+ // note the signature
+ if (item.returnType && item.params) {
+ functionStubSigs[item.ident] = Functions.getSignature(item.returnType.text, item.params.map(function(arg) { return arg.type }), false);
+ }
+
+ function addFromLibrary(ident) {
+ if (ident in addedLibraryItems) return '';
+ addedLibraryItems[ident] = true;
+
+ // dependencies can be JS functions, which we just run
+ if (typeof ident == 'function') return ident();
+
+ // Don't replace implemented functions with library ones (which can happen when we add dependencies).
+ // Note: We don't return the dependencies here. Be careful not to end up where this matters
+ if (('_' + ident) in Functions.implementedFunctions) return '';
+
+ var snippet = LibraryManager.library[ident];
+ var redirectedIdent = null;
+ var deps = LibraryManager.library[ident + '__deps'] || [];
+ var isFunction = false;
+
+ if (typeof snippet === 'string') {
+ var target = LibraryManager.library[snippet];
+ if (target) {
+ // Redirection for aliases. We include the parent, and at runtime make ourselves equal to it.
+ // This avoid having duplicate functions with identical content.
+ redirectedIdent = snippet;
+ deps.push(snippet);
+ snippet = '_' + snippet;
}
-
- var postsetId = ident + '__postset';
- var postset = LibraryManager.library[postsetId];
- if (postset && !addedLibraryItems[postsetId] && !SIDE_MODULE) {
- addedLibraryItems[postsetId] = true;
- ret.push({
- intertype: 'GlobalVariablePostSet',
- JS: postset
- });
+ // In asm, we need to know about library functions. If there is a target, though, then no
+ // need to consider this a library function - we will call directly to it anyhow
+ if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math\.\w+/.exec(snippet))) {
+ Functions.libraryFunctions[ident] = 1;
}
+ } else if (typeof snippet === 'object') {
+ snippet = stringifyWithFunctions(snippet);
+ } else if (typeof snippet === 'function') {
+ isFunction = true;
+ snippet = processLibraryFunction(snippet, ident);
+ if (ASM_JS) Functions.libraryFunctions[ident] = 1;
+ }
+
+ var postsetId = ident + '__postset';
+ var postset = LibraryManager.library[postsetId];
+ if (postset && !addedLibraryItems[postsetId] && !SIDE_MODULE) {
+ addedLibraryItems[postsetId] = true;
+ itemsDict.GlobalVariablePostSet.push({
+ intertype: 'GlobalVariablePostSet',
+ JS: postset
+ });
+ }
- if (redirectedIdent) {
- deps = deps.concat(LibraryManager.library[redirectedIdent + '__deps'] || []);
- }
- if (ASM_JS) {
- // In asm, dependencies implemented in C might be needed by JS library functions.
- // We don't know yet if they are implemented in C or not. To be safe, export such
- // special cases.
- [LIBRARY_DEPS_TO_AUTOEXPORT].forEach(function(special) {
- deps.forEach(function(dep) {
- if (dep == special && !EXPORTED_FUNCTIONS[dep]) {
- EXPORTED_FUNCTIONS[dep] = 1;
- }
- });
+ if (redirectedIdent) {
+ deps = deps.concat(LibraryManager.library[redirectedIdent + '__deps'] || []);
+ }
+ if (ASM_JS) {
+ // In asm, dependencies implemented in C might be needed by JS library functions.
+ // We don't know yet if they are implemented in C or not. To be safe, export such
+ // special cases.
+ [LIBRARY_DEPS_TO_AUTOEXPORT].forEach(function(special) {
+ deps.forEach(function(dep) {
+ if (dep == special && !EXPORTED_FUNCTIONS[dep]) {
+ EXPORTED_FUNCTIONS[dep] = 1;
+ }
});
+ });
+ }
+ // $ident's are special, we do not prefix them with a '_'.
+ if (ident[0] === '$') {
+ ident = ident.substr(1);
+ } else {
+ ident = '_' + ident;
+ }
+ if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps);
+ var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
+ var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';');
+ if (ASM_JS) {
+ var sig = LibraryManager.library[ident.substr(1) + '__sig'];
+ if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) {
+ // asm library function, add it as generated code alongside the generated code
+ Functions.implementedFunctions[ident] = sig;
+ asmLibraryFunctions.push(contentText);
+ contentText = ' ';
+ EXPORTED_FUNCTIONS[ident] = 1;
+ Functions.libraryFunctions[ident.substr(1)] = 2;
}
- // $ident's are special, we do not prefix them with a '_'.
- if (ident[0] === '$') {
- ident = ident.substr(1);
- } else {
- ident = '_' + ident;
- }
- if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps);
- var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
- var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';');
- if (ASM_JS) {
- var sig = LibraryManager.library[ident.substr(1) + '__sig'];
- if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) {
- // asm library function, add it as generated code alongside the generated code
- Functions.implementedFunctions[ident] = sig;
- asmLibraryFunctions.push(contentText);
- contentText = ' ';
- EXPORTED_FUNCTIONS[ident] = 1;
- Functions.libraryFunctions[ident.substr(1)] = 2;
- }
- }
- if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent
- if ((!ASM_JS || phase == 'pre') &&
- (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) {
- contentText += '\nModule["' + ident + '"] = ' + ident + ';';
- }
- return depsText + contentText;
}
+ if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent
+ if ((!ASM_JS || phase == 'pre') &&
+ (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) {
+ contentText += '\nModule["' + ident + '"] = ' + ident + ';';
+ }
+ return depsText + contentText;
+ }
- var ret = [item];
- if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return null;
- var shortident = item.ident.substr(1);
- if (BUILD_AS_SHARED_LIB) {
- // Shared libraries reuse the runtime of their parents.
- item.JS = '';
- } else {
- // If this is not linkable, anything not in the library is definitely missing
- var cancel = false;
- if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) {
- if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident);
- if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) printErr('warning: unresolved symbol: ' + shortident);
- if (ASM_JS || item.ident in DEAD_FUNCTIONS) {
- // emit a stub that will fail during runtime. this allows asm validation to succeed.
- LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
- } else {
- cancel = true; // emit nothing, not even var X = undefined;
- }
+ itemsDict.functionStub.push(item);
+ if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return;
+ var shortident = item.ident.substr(1);
+ if (BUILD_AS_SHARED_LIB) {
+ // Shared libraries reuse the runtime of their parents.
+ item.JS = '';
+ } else {
+ // If this is not linkable, anything not in the library is definitely missing
+ var cancel = false;
+ if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) {
+ if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident);
+ if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) printErr('warning: unresolved symbol: ' + shortident);
+ if (ASM_JS || item.ident in DEAD_FUNCTIONS) {
+ // emit a stub that will fail during runtime. this allows asm validation to succeed.
+ LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
+ } else {
+ cancel = true; // emit nothing, not even var X = undefined;
}
- item.JS = cancel ? ';' : addFromLibrary(shortident);
}
- return ret;
+ item.JS = cancel ? ';' : addFromLibrary(shortident);
}
- });
+ }
// function splitter
- substrate.addActor('FunctionSplitter', {
- processItem: function(item) {
- var ret = [item];
- item.splitItems = 0;
- item.labels.forEach(function(label) {
- label.lines.forEach(function(line) {
- line.func = item.ident;
- line.funcData = item; // TODO: remove all these, access it globally
- line.parentLabel = label.ident;
- ret.push(line);
- item.splitItems ++;
- });
- });
-
- this.forwardItems(ret, 'FuncLineTriager');
- }
- });
+ function functionSplitter(item) {
+ item.lines.forEach(function(line) {
+ Framework.currItem = line;
+ line.funcData = item; // TODO: remove all these, access it globally
+ switch (line.intertype) {
+ case 'value': line.JS = valueHandler(line); break;
+ case 'noop': line.JS = noopHandler(line); break;
+ case 'var': line.JS = varHandler(line); break;
+ case 'store': line.JS = storeHandler(line); break;
+ case 'deleted': line.JS = deletedHandler(line); break;
+ case 'branch': line.JS = branchHandler(line); break;
+ case 'switch': line.JS = switchHandler(line); break;
+ case 'return': line.JS = returnHandler(line); break;
+ case 'resume': line.JS = resumeHandler(line); break;
+ case 'invoke': line.JS = invokeHandler(line); break;
+ case 'atomic': line.JS = atomicHandler(line); break;
+ case 'landingpad': line.JS = landingpadHandler(line); break;
+ case 'load': line.JS = loadHandler(line); break;
+ case 'extractvalue': line.JS = extractvalueHandler(line); break;
+ case 'insertvalue': line.JS = insertvalueHandler(line); break;
+ case 'indirectbr': line.JS = indirectbrHandler(line); break;
+ case 'alloca': line.JS = allocaHandler(line); break;
+ case 'va_arg': line.JS = va_argHandler(line); break;
+ case 'mathop': line.JS = mathopHandler(line); break;
+ case 'bitcast': line.JS = bitcastHandler(line); break;
+ case 'getelementptr': line.JS = getelementptrHandler(line); break;
+ case 'call': line.JS = callHandler(line); break;
+ case 'unreachable': line.JS = unreachableHandler(line); break;
+ default: throw 'what is this line? ' + dump(line);
+ }
+ assert(line.JS);
+ if (line.assignTo) makeAssign(line);
+ Framework.currItem = null;
+ });
+ functionReconstructor(item);
+ }
// function for filtering functions for label debugging
if (LABEL_FUNCTION_FILTERS.length > 0) {
@@ -567,322 +557,297 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// function reconstructor & post-JS optimizer
- substrate.addActor('FunctionReconstructor', {
- funcs: {},
- seen: {},
- processItem: function(item) {
- if (this.seen[item.__uid__]) return null;
- if (item.intertype == 'function') {
- this.funcs[item.ident] = item;
- item.relines = {};
- this.seen[item.__uid__] = true;
- return null;
- }
- var line = item;
- var func = this.funcs[line.func];
- if (!func) return null;
-
- // Re-insert our line
- this.seen[item.__uid__] = true;
- var label = func.labels.filter(function(label) { return label.ident == line.parentLabel })[0];
- label.lines = label.lines.map(function(line2) {
- return (line2.lineNum !== line.lineNum) ? line2 : line;
- });
- func.splitItems --;
- // OLD delete line.funcData; // clean up
- if (func.splitItems > 0) return null;
+ function functionReconstructor(func) {
+ // We have this function all reconstructed, go and finalize it's JS!
- // We have this function all reconstructed, go and finalize it's JS!
+ if (IGNORED_FUNCTIONS.indexOf(func.ident) >= 0) return null;
- if (IGNORED_FUNCTIONS.indexOf(func.ident) >= 0) return null;
+ func.JS = '\n';
- func.JS = '\n';
+ var paramIdents = func.params.map(function(param) {
+ return toNiceIdent(param.ident);
+ });
- var paramIdents = func.params.map(function(param) {
- return toNiceIdent(param.ident);
+ if (CLOSURE_ANNOTATIONS) {
+ func.JS += '/**\n';
+ paramIdents.forEach(function(param) {
+ func.JS += ' * @param {number} ' + param + '\n';
});
+ func.JS += ' * @return {number}\n'
+ func.JS += ' */\n';
+ }
- if (CLOSURE_ANNOTATIONS) {
- func.JS += '/**\n';
- paramIdents.forEach(function(param) {
- func.JS += ' * @param {number} ' + param + '\n';
- });
- func.JS += ' * @return {number}\n'
- func.JS += ' */\n';
- }
-
- if (PRINT_SPLIT_FILE_MARKER) {
- func.JS += '\n//FUNCTION_BEGIN_MARKER\n'
- var associatedSourceFile = "NO_SOURCE";
- }
-
- if (DLOPEN_SUPPORT) Functions.getIndex(func.ident);
+ if (PRINT_SPLIT_FILE_MARKER) {
+ func.JS += '\n//FUNCTION_BEGIN_MARKER\n'
+ var associatedSourceFile = "NO_SOURCE";
+ }
+
+ if (DLOPEN_SUPPORT) Functions.getIndex(func.ident);
- func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
+ func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
- if (PGO) {
- func.JS += INDENTATION + 'PGOMonitor.called["' + func.ident + '"] = 1;\n';
- }
+ if (PGO) {
+ func.JS += INDENTATION + 'PGOMonitor.called["' + func.ident + '"] = 1;\n';
+ }
- if (ASM_JS) {
- // spell out argument types
- func.params.forEach(function(param) {
- func.JS += INDENTATION + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n';
- });
+ if (ASM_JS) {
+ // spell out argument types
+ func.params.forEach(function(param) {
+ func.JS += INDENTATION + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n';
+ });
- // spell out local variables
- var vars = values(func.variables).filter(function(v) { return v.origin != 'funcparam' });
- if (vars.length > 0) {
- var chunkSize = 8;
- var chunks = [];
- var i = 0;
- while (i < vars.length) {
- chunks.push(vars.slice(i, i+chunkSize));
- i += chunkSize;
- }
- for (i = 0; i < chunks.length; i++) {
- func.JS += INDENTATION + 'var ' + chunks[i].map(function(v) {
- var type = getImplementationType(v);
- if (!isIllegalType(type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal
- return v.ident + ' = ' + asmInitializer(type); //, func.variables[v.ident].impl);
- } else {
- return range(Math.ceil(getBits(type)/32)).map(function(i) {
- return v.ident + '$' + i + '= 0';
- }).join(',');
- }
- }).join(', ') + ';\n';
- }
+ // spell out local variables
+ var vars = values(func.variables).filter(function(v) { return v.origin != 'funcparam' });
+ if (vars.length > 0) {
+ var chunkSize = 8;
+ var chunks = [];
+ var i = 0;
+ while (i < vars.length) {
+ chunks.push(vars.slice(i, i+chunkSize));
+ i += chunkSize;
+ }
+ for (i = 0; i < chunks.length; i++) {
+ func.JS += INDENTATION + 'var ' + chunks[i].map(function(v) {
+ var type = getImplementationType(v);
+ if (!isIllegalType(type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal
+ return v.ident + ' = ' + asmInitializer(type); //, func.variables[v.ident].impl);
+ } else {
+ return range(Math.ceil(getBits(type)/32)).map(function(i) {
+ return v.ident + '$' + i + '= 0';
+ }).join(',');
+ }
+ }).join(', ') + ';\n';
}
}
+ }
- if (true) { // TODO: optimize away when not needed
- if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
- func.JS += INDENTATION + 'var label = 0;\n';
- }
+ if (true) { // TODO: optimize away when not needed
+ if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
+ func.JS += INDENTATION + 'var label = 0;\n';
+ }
- if (ASM_JS) {
- var hasByVal = false;
- func.params.forEach(function(param) {
- hasByVal = hasByVal || param.byVal;
- });
- if (hasByVal) {
- func.JS += INDENTATION + 'var tempParam = 0;\n';
- }
+ if (ASM_JS) {
+ var hasByVal = false;
+ func.params.forEach(function(param) {
+ hasByVal = hasByVal || param.byVal;
+ });
+ if (hasByVal) {
+ func.JS += INDENTATION + 'var tempParam = 0;\n';
}
+ }
+
+ if (func.hasVarArgsCall) {
+ func.JS += INDENTATION + 'var tempVarArgs = 0;\n';
+ }
+
+ // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up.
+ func.JS += INDENTATION + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n';
- if (func.hasVarArgsCall) {
- func.JS += INDENTATION + 'var tempVarArgs = 0;\n';
+ // Make copies of by-value params
+ // XXX It is not clear we actually need this. While without this we fail, it does look like
+ // Clang normally does the copy itself, in the calling function. We only need this code
+ // when Clang optimizes the code and passes the original, not the copy, to the other
+ // function. But Clang still copies, the copy is just unused! Need to figure out if that
+ // is caused by our running just some optimizations (the safe ones), or if its a bug
+ // in Clang, or a bug in our understanding of the IR.
+ func.params.forEach(function(param) {
+ if (param.byVal) {
+ var type = removePointing(param.type);
+ var typeInfo = Types.types[type];
+ func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' +
+ makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n';
}
+ });
- // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up.
- func.JS += INDENTATION + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n';
+ if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\n";
- // Make copies of by-value params
- // XXX It is not clear we actually need this. While without this we fail, it does look like
- // Clang normally does the copy itself, in the calling function. We only need this code
- // when Clang optimizes the code and passes the original, not the copy, to the other
- // function. But Clang still copies, the copy is just unused! Need to figure out if that
- // is caused by our running just some optimizations (the safe ones), or if its a bug
- // in Clang, or a bug in our understanding of the IR.
- func.params.forEach(function(param) {
- if (param.byVal) {
- var type = removePointing(param.type);
- var typeInfo = Types.types[type];
- func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' +
- makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n';
+ // Walk function blocks and generate JS
+ function walkBlock(block, indent) {
+ if (!block) return '';
+ dprint('relooping', 'walking block: ' + block.type + ',' + block.entries + ' : ' + block.labels.length);
+ function getLabelLines(label, indent, relooping) {
+ if (!label) return '';
+ var ret = '';
+ if ((LABEL_DEBUG >= 2) && functionNameFilterTest(func.ident)) {
+ ret += indent + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n";
}
- });
-
- if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\n";
-
- // Walk function blocks and generate JS
- function walkBlock(block, indent) {
- if (!block) return '';
- dprint('relooping', 'walking block: ' + block.type + ',' + block.entries + ' : ' + block.labels.length);
- function getLabelLines(label, indent, relooping) {
- if (!label) return '';
- var ret = '';
- if ((LABEL_DEBUG >= 2) && functionNameFilterTest(func.ident)) {
- ret += indent + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n";
- }
- if (EXECUTION_TIMEOUT > 0) {
- ret += indent + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n';
- }
-
- if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(line.lineNum)) {
- // Overwrite the associated source file for every line. The last line should contain the source file associated to
- // the return value/address of outer most block (the marked function).
- associatedSourceFile = Debugging.getAssociatedSourceFile(line.lineNum);
- }
-
- // for special labels we care about (for phi), mark that we visited them
- var i = 0;
- return ret + label.lines.map(function(line) {
- var JS = line.JS;
- if (relooping && i == label.lines.length-1) {
- if (line.intertype == 'branch' || line.intertype == 'switch') {
- JS = ''; // just branching operations - done in the relooper, so nothing need be done here
- } else if (line.intertype == 'invoke') {
- JS = line.reloopingJS; // invokes have code that is not rendered in the relooper (the call inside a try-catch)
- }
- }
- i++;
- // invoke instructions span two lines, and the debug info is located
- // on the second line, hence the +1
- return JS + (Debugging.on ? Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)) : '');
- })
- .join('\n')
- .split('\n') // some lines include line breaks
- .map(function(line) { return indent + line })
- .join('\n');
+ if (EXECUTION_TIMEOUT > 0) {
+ ret += indent + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n';
}
- var ret = '';
- if (!RELOOP || func.forceEmulated) { // TODO: also if just 1 label?
- if (block.labels.length > 1) {
- if (block.entries.length == 1) {
- ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
- } // otherwise, should have been set before!
- if (func.setjmpTable) {
- if (!ASM_JS) {
- var setjmpTable = {};
- ret += indent + 'var mySetjmpIds = {};\n';
- ret += indent + 'var setjmpTable = {';
- func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
- ret += '"' + getLabelId(triple.oldLabel) + '": ' + 'function(value) { label = ' + getLabelId(triple.newLabel) + '; ' + triple.assignTo + ' = value },';
- });
- ret += 'dummy: 0';
- ret += '};\n';
- } else {
- ret += 'var setjmpLabel = 0;\n';
- ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n';
- ret += makeSetValue('setjmpTable', '0', '0', 'i32') + ';'; // initialize first entry to 0
- }
- }
- ret += indent + 'while(1) ';
- if (func.setjmpTable && !ASM_JS) {
- ret += 'try { ';
- }
- ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n';
- ret += block.labels.map(function(label) {
- return indent + INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
- + getLabelLines(label, indent + INDENTATION + INDENTATION);
- }).join('\n') + '\n';
- if (func.setjmpTable && ASM_JS) {
- // emit a label in which we write to the proper local variable, before jumping to the actual label
- ret += INDENTATION + 'case ' + SETJMP_LABEL + ': ';
- ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
- return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n';
- }).join(' else ');
- if (ASSERTIONS) ret += 'else abort(-3);\n';
- ret += '__THREW__ = threwValue = 0;\n';
- ret += 'break;\n';
- }
- if (ASSERTIONS) ret += indent + INDENTATION + 'default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
- ret += indent + '}\n';
- if (func.setjmpTable && !ASM_JS) {
- ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
+
+ if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum)) {
+ // Overwrite the associated source file for every line. The last line should contain the source file associated to
+ // the return value/address of outer most block (the marked function).
+ associatedSourceFile = Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum);
+ }
+
+ // for special labels we care about (for phi), mark that we visited them
+ var i = 0;
+ return ret + label.lines.map(function(line) {
+ var JS = line.JS;
+ if (relooping && i == label.lines.length-1) {
+ if (line.intertype == 'branch' || line.intertype == 'switch') {
+ JS = ''; // just branching operations - done in the relooper, so nothing need be done here
+ } else if (line.intertype == 'invoke') {
+ JS = line.reloopingJS; // invokes have code that is not rendered in the relooper (the call inside a try-catch)
}
- } else {
- ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
}
- ret += '\n';
- } else {
- // Reloop multiple blocks using the compiled relooper
-
- //Relooper.setDebug(1);
- Relooper.init();
-
- if (ASM_JS) Relooper.setAsmJSMode(1);
-
- var blockMap = {};
- // add blocks
- for (var i = 0; i < block.labels.length; i++) {
- var label = block.labels[i];
- var content = getLabelLines(label, '', true);
- //printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n');
- var last = label.lines[label.lines.length-1];
- if (!last.signedIdent) {
- blockMap[label.ident] = Relooper.addBlock(content);
+ i++;
+ // invoke instructions span two lines, and the debug info is located
+ // on the second line, hence the +1
+ return JS + (Debugging.on ? Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)) : '');
+ })
+ .join('\n')
+ .split('\n') // some lines include line breaks
+ .map(function(line) { return indent + line })
+ .join('\n');
+ }
+ var ret = '';
+ if (!RELOOP || func.forceEmulated) { // TODO: also if just 1 label?
+ if (block.labels.length > 1) {
+ if (block.entries.length == 1) {
+ ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
+ } // otherwise, should have been set before!
+ if (func.setjmpTable) {
+ if (!ASM_JS) {
+ var setjmpTable = {};
+ ret += indent + 'var mySetjmpIds = {};\n';
+ ret += indent + 'var setjmpTable = {';
+ func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
+ ret += '"' + getLabelId(triple.oldLabel) + '": ' + 'function(value) { label = ' + getLabelId(triple.newLabel) + '; ' + triple.assignTo + ' = value },';
+ });
+ ret += 'dummy: 0';
+ ret += '};\n';
} else {
- assert(last.intertype == 'switch');
- blockMap[label.ident] = Relooper.addBlock(content, last.signedIdent);
+ ret += 'var setjmpLabel = 0;\n';
+ ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n';
+ ret += makeSetValue('setjmpTable', '0', '0', 'i32') + ';'; // initialize first entry to 0
}
}
- // add branchings
- function relevant(x) { return x && x.length > 2 ? x : 0 } // ignores ';' which valueJS and label*JS can be if empty
- for (var i = 0; i < block.labels.length; i++) {
- var label = block.labels[i];
- var ident = label.ident;
- var last = label.lines[label.lines.length-1];
- //printErr('zz last ' + dump(last));
- if (last.intertype == 'branch') {
- if (last.label) { // 1 target
- Relooper.addBranch(blockMap[ident], blockMap[last.label], 0, relevant(last.labelJS));
- } else { // 2 targets
- Relooper.addBranch(blockMap[ident], blockMap[last.labelTrue], last.valueJS, relevant(last.labelTrueJS));
- Relooper.addBranch(blockMap[ident], blockMap[last.labelFalse], 0, relevant(last.labelFalseJS));
- }
- } else if (last.intertype == 'switch') {
- last.groupedLabels.forEach(function(switchLabel) {
- Relooper.addBranch(blockMap[ident], blockMap[switchLabel.label], switchLabel.value, relevant(switchLabel.labelJS));
- });
- Relooper.addBranch(blockMap[ident], blockMap[last.defaultLabel], 0, relevant(last.defaultLabelJS));
- } else if (last.intertype == 'invoke') {
- Relooper.addBranch(blockMap[ident], blockMap[last.toLabel], '!__THREW__', relevant(last.toLabelJS));
- Relooper.addBranch(blockMap[ident], blockMap[last.unwindLabel], 0, relevant(last.unwindLabelJS));
- } else if (last.intertype in RELOOP_IGNORED_LASTS) {
- } else {
- throw 'unknown reloop last line: ' + last.intertype;
+ ret += indent + 'while(1) ';
+ if (func.setjmpTable && !ASM_JS) {
+ ret += 'try { ';
+ }
+ ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n';
+ ret += block.labels.map(function(label) {
+ return indent + INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ + getLabelLines(label, indent + INDENTATION + INDENTATION);
+ }).join('\n') + '\n';
+ if (func.setjmpTable && ASM_JS) {
+ // emit a label in which we write to the proper local variable, before jumping to the actual label
+ ret += INDENTATION + 'case ' + SETJMP_LABEL + ': ';
+ ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
+ return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n';
+ }).join(' else ');
+ if (ASSERTIONS) ret += 'else abort(-3);\n';
+ ret += '__THREW__ = threwValue = 0;\n';
+ ret += 'break;\n';
+ }
+ if (ASSERTIONS) ret += indent + INDENTATION + 'default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
+ ret += indent + '}\n';
+ if (func.setjmpTable && !ASM_JS) {
+ ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
+ }
+ } else {
+ ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
+ }
+ ret += '\n';
+ } else {
+ // Reloop multiple blocks using the compiled relooper
+
+ //Relooper.setDebug(1);
+ Relooper.init();
+
+ if (ASM_JS) Relooper.setAsmJSMode(1);
+
+ var blockMap = {};
+ // add blocks
+ for (var i = 0; i < block.labels.length; i++) {
+ var label = block.labels[i];
+ var content = getLabelLines(label, '', true);
+ //printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n');
+ var last = label.lines[label.lines.length-1];
+ if (!last.signedIdent) {
+ blockMap[label.ident] = Relooper.addBlock(content);
+ } else {
+ assert(last.intertype == 'switch');
+ blockMap[label.ident] = Relooper.addBlock(content, last.signedIdent);
+ }
+ }
+ // add branchings
+ function relevant(x) { return x && x.length > 2 ? x : 0 } // ignores ';' which valueJS and label*JS can be if empty
+ for (var i = 0; i < block.labels.length; i++) {
+ var label = block.labels[i];
+ var ident = label.ident;
+ var last = label.lines[label.lines.length-1];
+ //printErr('zz last ' + dump(last));
+ if (last.intertype == 'branch') {
+ if (last.label) { // 1 target
+ Relooper.addBranch(blockMap[ident], blockMap[last.label], 0, relevant(last.labelJS));
+ } else { // 2 targets
+ Relooper.addBranch(blockMap[ident], blockMap[last.labelTrue], last.valueJS, relevant(last.labelTrueJS));
+ Relooper.addBranch(blockMap[ident], blockMap[last.labelFalse], 0, relevant(last.labelFalseJS));
}
+ } else if (last.intertype == 'switch') {
+ last.groupedLabels.forEach(function(switchLabel) {
+ Relooper.addBranch(blockMap[ident], blockMap[switchLabel.label], switchLabel.value, relevant(switchLabel.labelJS));
+ });
+ Relooper.addBranch(blockMap[ident], blockMap[last.defaultLabel], 0, relevant(last.defaultLabelJS));
+ } else if (last.intertype == 'invoke') {
+ Relooper.addBranch(blockMap[ident], blockMap[last.toLabel], '!__THREW__', relevant(last.toLabelJS));
+ Relooper.addBranch(blockMap[ident], blockMap[last.unwindLabel], 0, relevant(last.unwindLabelJS));
+ } else if (last.intertype in RELOOP_IGNORED_LASTS) {
+ } else {
+ throw 'unknown reloop last line: ' + last.intertype;
}
- ret += Relooper.render(blockMap[block.entries[0]]);
}
- return ret;
- }
- func.JS += walkBlock(func.block, INDENTATION);
- // Finalize function
- if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n";
- // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts())
- if (RELOOP && func.lines.length > 0 && func.returnType != 'void') {
- var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length;
- if (returns == 0) func.JS += INDENTATION + 'return ' + asmCoercion('0', func.returnType);
- }
- func.JS += '}\n';
-
- if (PRINT_SPLIT_FILE_MARKER) {
- func.JS += '\n//FUNCTION_END_MARKER_OF_SOURCE_FILE_' + associatedSourceFile + '\n';
+ ret += Relooper.render(blockMap[block.entries[0]]);
+ Relooper.cleanup();
}
+ return ret;
+ }
+ func.JS += walkBlock(func.block, INDENTATION);
+ // Finalize function
+ if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n";
+ // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts())
+ if (RELOOP && func.lines.length > 0 && func.returnType != 'void') {
+ var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length;
+ if (returns == 0) func.JS += INDENTATION + 'return ' + asmCoercion('0', func.returnType);
+ }
+ func.JS += '}\n';
+
+ if (PRINT_SPLIT_FILE_MARKER) {
+ func.JS += '\n//FUNCTION_END_MARKER_OF_SOURCE_FILE_' + associatedSourceFile + '\n';
+ }
- if (!ASM_JS && (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS))) {
- func.JS += 'Module["' + func.ident + '"] = ' + func.ident + ';';
- }
+ if (!ASM_JS && (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS))) {
+ func.JS += 'Module["' + func.ident + '"] = ' + func.ident + ';';
+ }
- if (!ASM_JS && INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) {
- func.JS += func.ident + '["X"]=1;';
- }
+ if (!ASM_JS && INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) {
+ func.JS += func.ident + '["X"]=1;';
+ }
- if (BUILD_AS_SHARED_LIB == 2) {
- // TODO: make the assert conditional on ASSERTIONS
- func.JS += 'if (globalScope) { assert(!globalScope["' + func.ident + '"]); globalScope["' + func.ident + '"] = ' + func.ident + ' }';
- }
+ if (BUILD_AS_SHARED_LIB == 2) {
+ // TODO: make the assert conditional on ASSERTIONS
+ func.JS += 'if (globalScope) { assert(!globalScope["' + func.ident + '"]); globalScope["' + func.ident + '"] = ' + func.ident + ' }';
+ }
- func.JS = func.JS.replace(/\n *;/g, '\n'); // remove unneeded lines
+ func.JS = func.JS.replace(/\n *;/g, '\n'); // remove unneeded lines
- if (MAIN_MODULE || SIDE_MODULE) {
- // Clone the function for each of its aliases. We do not know which name it will be used by in another module,
- // and we do not have a heavyweight metadata system to resolve aliases during linking
- var aliases = Functions.aliases[func.ident];
- if (aliases) {
- var body = func.JS.substr(func.JS.indexOf('('));
- aliases.forEach(function(alias) {
- func.JS += '\n' + 'function ' + alias + body;
- });
- }
+ if (MAIN_MODULE || SIDE_MODULE) {
+ // Clone the function for each of its aliases. We do not know which name it will be used by in another module,
+ // and we do not have a heavyweight metadata system to resolve aliases during linking
+ var aliases = Functions.aliases[func.ident];
+ if (aliases) {
+ var body = func.JS.substr(func.JS.indexOf('('));
+ aliases.forEach(function(alias) {
+ func.JS += '\n' + 'function ' + alias + body;
+ });
}
-
- return func;
}
- });
+ itemsDict.function.push(func);
+ }
function getVarData(funcData, ident) {
var local = funcData.variables[ident];
@@ -898,18 +863,6 @@ function JSify(data, functionsOnly, givenFunctions) {
return data.impl;
}
- substrate.addActor('FuncLineTriager', {
- processItem: function(item) {
- if (item.intertype == 'function') {
- this.forwardItem(item, 'FunctionReconstructor'); // XXX not really needed
- } else if (item.JS) {
- this.forwardItem(item, 'FunctionReconstructor'); // XXX not really needed
- } else {
- this.forwardItem(item, 'Intertype:' + item.intertype);
- }
- }
- });
-
// An interitem that has |assignTo| is an assign to that item. They call this function which
// generates the actual assignment.
function makeAssign(item) {
@@ -944,29 +897,16 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// Function lines
- function makeFuncLineActor(intertype, func) {
- return substrate.addActor('Intertype:' + intertype, {
- processItem: function(item) {
- item.JS = func(item);
- if (!item.JS) throw "No JS generated for " + dump((item.funcData=null,item));
- if (item.assignTo) {
- makeAssign(item);
- if (!item.JS) throw "No assign JS generated for " + dump(item);
- }
- this.forwardItem(item, 'FunctionReconstructor');
- }
- });
- }
- makeFuncLineActor('value', function(item) {
+ function valueHandler(item) {
return item.ident;
- });
- makeFuncLineActor('noop', function(item) {
+ }
+ function noopHandler(item) {
return ';';
- });
- makeFuncLineActor('var', function(item) { // assigns into phis become simple vars
+ }
+ function varHandler(item) { // assigns into phis become simple vars
return ASM_JS ? ';' : ('var ' + item.ident + ';');
- });
- makeFuncLineActor('store', function(item) {
+ }
+ function storeHandler(item) {
var value = finalizeLLVMParameter(item.value);
if (pointingLevels(item.pointerType) == 1) {
value = parseNumerical(value, item.valueType);
@@ -998,9 +938,9 @@ function JSify(data, functionsOnly, givenFunctions) {
throw 'unknown [store] impl: ' + impl;
}
return null;
- });
+ }
- makeFuncLineActor('deleted', function(item) { return ';' });
+ function deletedHandler(item) { return ';' }
function getOriginalLabelId(label) {
var funcData = Framework.currItem.funcData;
@@ -1113,7 +1053,7 @@ function JSify(data, functionsOnly, givenFunctions) {
*/
}
- makeFuncLineActor('branch', function(item) {
+ function branchHandler(item) {
var phiSets = calcPhiSets(item);
if (!item.value) {
return (item.labelJS = getPhiSetsForLabel(phiSets, item.label)) + makeBranch(item.label, item.currLabelId);
@@ -1134,8 +1074,8 @@ function JSify(data, functionsOnly, givenFunctions) {
return head + labelTrue + else_ + labelFalse + tail;
}
}
- });
- makeFuncLineActor('switch', function(item) {
+ }
+ function switchHandler(item) {
// use a switch if the range is not too big or sparse
var minn = Infinity, maxx = -Infinity;
item.switchLabels.forEach(function(switchLabel) {
@@ -1174,7 +1114,8 @@ function JSify(data, functionsOnly, givenFunctions) {
if (!useIfs) {
ret += 'switch(' + signedIdent + ') {\n';
}
- for (var targetLabel in targetLabels) {
+ // process target labels, sorting them so output is consistently ordered
+ keys(targetLabels).sort().forEach(function(targetLabel) {
if (!first && useIfs) {
ret += 'else ';
} else {
@@ -1202,7 +1143,7 @@ function JSify(data, functionsOnly, givenFunctions) {
labelJS: phiSet
});
}
- }
+ });
var phiSet = item.defaultLabelJS = getPhiSetsForLabel(phiSets, item.defaultLabel);
if (useIfs) {
if (item.switchLabels.length > 0) ret += 'else {\n';
@@ -1219,8 +1160,8 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += ' ' + toNiceIdent(item.value);
}
return ret;
- });
- makeFuncLineActor('return', function(item) {
+ }
+ function returnHandler(item) {
var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations) + ';\n';
if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) {
ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n"
@@ -1233,9 +1174,9 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += ' ' + asmCoercion(value, item.type);
}
return ret + ';';
- });
- makeFuncLineActor('resume', function(item) {
- if (DISABLE_EXCEPTION_CATCHING) return 'abort()';
+ }
+ function resumeHandler(item) {
+ if (DISABLE_EXCEPTION_CATCHING && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST)) return 'abort()';
if (item.ident == 0) {
// No exception to resume, so we can just bail.
// This is related to issue #917 and http://llvm.org/PR15518
@@ -1244,8 +1185,8 @@ function JSify(data, functionsOnly, givenFunctions) {
// If there is no current exception, set this one as it (during a resume, the current exception can be wiped out)
var ptr = makeStructuralAccess(item.ident, 0);
return '___resumeException(' + asmCoercion(ptr, 'i32') + ')';
- });
- makeFuncLineActor('invoke', function(item) {
+ }
+ function invokeHandler(item) {
// Wrapping in a function lets us easily return values if we are
// in an assignment
var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST);
@@ -1291,14 +1232,14 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += 'if (!__THREW__) { ' + item.toLabelJS + makeBranch(item.toLabel, item.currLabelId)
+ ' } else { ' + item.unwindLabelJS + makeBranch(item.unwindLabel, item.currLabelId) + ' }';
return ret;
- });
- makeFuncLineActor('atomic', function(item) {
+ }
+ function atomicHandler(item) {
var type = item.params[0].type;
var param1 = finalizeLLVMParameter(item.params[0]);
var param2 = finalizeLLVMParameter(item.params[1]);
switch (item.op) {
- case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type, null, null, null, null, ',') + ',tempValue)';
- case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type, null, null, null, null, ',') + ',tempValue)';
+ case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, asmCoercion('tempValue+' + param2, type), type, null, null, null, null, ',') + ',tempValue)';
+ case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, asmCoercion('tempValue-' + param2, type), type, null, null, null, null, ',') + ',tempValue)';
case 'or': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue|' + param2, type, null, null, null, null, ',') + ',tempValue)';
case 'and': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue&' + param2, type, null, null, null, null, ',') + ',tempValue)';
case 'xor': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue^' + param2, type, null, null, null, null, ',') + ',tempValue)';
@@ -1309,9 +1250,9 @@ function JSify(data, functionsOnly, givenFunctions) {
}
default: throw 'unhandled atomic op: ' + item.op;
}
- });
- makeFuncLineActor('landingpad', function(item) {
- if (DISABLE_EXCEPTION_CATCHING && USE_TYPED_ARRAYS == 2) {
+ }
+ function landingpadHandler(item) {
+ if (DISABLE_EXCEPTION_CATCHING && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST) && USE_TYPED_ARRAYS == 2) {
ret = makeVarDef(item.assignTo) + '$0 = 0; ' + item.assignTo + '$1 = 0;';
item.assignTo = null;
if (VERBOSE) warnOnce('landingpad, but exceptions are disabled!');
@@ -1324,18 +1265,18 @@ function JSify(data, functionsOnly, givenFunctions) {
item.assignTo = null;
}
return ret;
- });
- makeFuncLineActor('load', function(item) {
+ }
+ function loadHandler(item) {
var value = finalizeLLVMParameter(item.pointer);
var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED;
switch (impl) {
case VAR_NATIVIZED: {
if (isNumber(item.ident)) {
- item.assignTo = null;
// Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source
if (ASM_JS) {
- return 'abort(' + item.ident + ')';
+ return asmCoercion('abort(' + item.ident + ')', item.type);
} else {
+ item.assignTo = null;
return 'throw "fault on read from ' + item.ident + '";';
}
}
@@ -1344,8 +1285,8 @@ function JSify(data, functionsOnly, givenFunctions) {
case VAR_EMULATED: return makeGetValue(value, 0, item.type, 0, item.unsigned, 0, item.align);
default: throw "unknown [load] impl: " + impl;
}
- });
- makeFuncLineActor('extractvalue', function(item) {
+ }
+ function extractvalueHandler(item) {
assert(item.indexes.length == 1); // TODO: use getelementptr parsing stuff, for depth. For now, we assume that LLVM aggregates are flat,
// and we emulate them using simple JS objects { f1: , f2: , } etc., for speed
var index = item.indexes[0][0].text;
@@ -1358,8 +1299,8 @@ function JSify(data, functionsOnly, givenFunctions) {
return 'var ' + assignTo + '$0 = ' + item.ident + '.f' + index + '[0];' +
'var ' + assignTo + '$1 = ' + item.ident + '.f' + index + '[1];';
}
- });
- makeFuncLineActor('insertvalue', function(item) {
+ }
+ function insertvalueHandler(item) {
assert(item.indexes.length == 1); // TODO: see extractvalue
var ret = '(', ident;
if (item.ident === '0') {
@@ -1367,24 +1308,24 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += item.ident + ' = [' + makeEmptyStruct(item.type) + '], ';
}
return ret + item.ident + '.f' + item.indexes[0][0].text + ' = ' + finalizeLLVMParameter(item.value) + ', ' + item.ident + ')';
- });
- makeFuncLineActor('indirectbr', function(item) {
+ }
+ function indirectbrHandler(item) {
var phiSets = calcPhiSets(item);
var js = 'var ibr = ' + finalizeLLVMParameter(item.value) + ';\n';
for (var targetLabel in phiSets) {
js += 'if (' + makeComparison('ibr', '==', targetLabel, 'i32') + ') { ' + getPhiSetsForLabel(phiSets, targetLabel) + ' }\n';
}
return js + makeBranch('ibr', item.currLabelId, true);
- });
- makeFuncLineActor('alloca', function(item) {
+ }
+ function allocaHandler(item) {
if (typeof item.allocatedIndex === 'number') {
if (item.allocatedSize === 0) return ''; // This will not actually be shown - it's nativized
return asmCoercion(getFastValue('sp', '+', item.allocatedIndex.toString()), 'i32');
} else {
return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum));
}
- });
- makeFuncLineActor('va_arg', function(item) {
+ }
+ function va_argHandler(item) {
assert(TARGET_LE32);
var ident = item.value.ident;
var move = Runtime.STACK_ALIGN;
@@ -1393,11 +1334,11 @@ function JSify(data, functionsOnly, givenFunctions) {
return '(tempInt=' + makeGetValue(ident, Runtime.QUANTUM_SIZE, '*') + ',' +
makeSetValue(ident, Runtime.QUANTUM_SIZE, 'tempInt + ' + move, '*') + ',' +
makeGetValue(makeGetValue(ident, 0, '*'), 'tempInt', item.type) + ')';
- });
+ }
- makeFuncLineActor('mathop', processMathop);
+ var mathopHandler = processMathop;
- makeFuncLineActor('bitcast', function(item) {
+ function bitcastHandler(item) {
var temp = {
op: 'bitcast', variant: null, type: item.type,
assignTo: item.assignTo,
@@ -1406,7 +1347,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var ret = processMathop(temp);
if (!temp.assignTo) item.assignTo = null; // If the assign was stolen, propagate that
return ret;
- });
+ }
function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn, invoke) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
@@ -1614,33 +1555,26 @@ function JSify(data, functionsOnly, givenFunctions) {
return js;
}
- makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) });
- makeFuncLineActor('call', function(item) {
+ function getelementptrHandler(item) { return finalizeLLVMFunctionCall(item) }
+ function callHandler(item) {
if (item.standalone && LibraryManager.isStubFunction(item.ident)) return ';';
var ret = makeFunctionCall(item.ident, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : '');
return makeVarArgsCleanup(ret);
- });
+ }
- makeFuncLineActor('unreachable', function(item) {
+ function unreachableHandler(item) {
var ret = '';
if (ASM_JS && item.funcData.returnType != 'void') ret = 'return ' + asmCoercion('0', item.funcData.returnType) + ';';
if (ASSERTIONS) {
ret = (ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"') + ';' + ret;
}
return ret || ';';
- });
+ }
// Final combiner
- function finalCombiner(items) {
+ function finalCombiner() {
dprint('unparsedFunctions', 'Starting finalCombiner');
- var itemsDict = { type: [], GlobalVariableStub: [], functionStub: [], function: [], GlobalVariable: [], GlobalVariablePostSet: [] };
- items.forEach(function(item) {
- item.lines = null;
- var small = { intertype: item.intertype, JS: item.JS, ident: item.ident, dependencies: item.dependencies }; // Release memory
- itemsDict[small.intertype].push(small);
- });
- items = null;
var splitPostSets = splitter(itemsDict.GlobalVariablePostSet, function(x) { return x.ident && x.dependencies });
itemsDict.GlobalVariablePostSet = splitPostSets.leftIn;
@@ -1897,7 +1831,7 @@ function JSify(data, functionsOnly, givenFunctions) {
Functions.implementedFunctions[func.ident] = Functions.getSignature(func.returnType, func.params.map(function(param) { return param.type }));
});
}
- substrate.addItems(data.functionStubs, 'FunctionStub');
+ data.functionStubs.forEach(functionStubHandler);
assert(data.functions.length == 0);
} else {
if (phase == 'pre') {
@@ -1906,21 +1840,21 @@ function JSify(data, functionsOnly, givenFunctions) {
data.globalVariables._llvm_global_ctors.ctors.unshift('runPostSets'); // run postsets right before global initializers
hasCtors = true;
} else {
- substrate.addItems([{
+ globalVariableHandler({
intertype: 'GlobalVariableStub',
ident: '_llvm_global_ctors',
type: '[1 x { i32, void ()* }]',
ctors: ["runPostSets"],
- }], 'GlobalVariable');
+ });
}
}
- substrate.addItems(sortGlobals(data.globalVariables), 'GlobalVariable');
- substrate.addItems(data.aliass, 'Alias');
- substrate.addItems(data.functions, 'FunctionSplitter');
+ sortGlobals(data.globalVariables).forEach(globalVariableHandler);
+ data.aliass.forEach(aliasHandler);
+ data.functions.forEach(functionSplitter);
}
- finalCombiner(substrate.solve());
+ finalCombiner();
dprint('framework', 'Big picture: Finishing JSifier, main pass=' + mainPass);
}
diff --git a/src/library.js b/src/library.js
index 7a144951..abee70c4 100644
--- a/src/library.js
+++ b/src/library.js
@@ -628,24 +628,13 @@ LibraryManager.library = {
// http://pubs.opengroup.org/onlinepubs/000095399/functions/chdir.html
// NOTE: The path argument may be a string, to simplify fchdir().
if (typeof path !== 'string') path = Pointer_stringify(path);
- var lookup;
try {
- lookup = FS.lookupPath(path, { follow: true });
+ FS.chdir(path);
+ return 0;
} catch (e) {
FS.handleFSError(e);
return -1;
}
- if (!FS.isDir(lookup.node.mode)) {
- ___setErrNo(ERRNO_CODES.ENOTDIR);
- return -1;
- }
- var err = FS.nodePermissions(lookup.node, 'x');
- if (err) {
- ___setErrNo(err);
- return -1;
- }
- FS.currentPath = lookup.path;
- return 0;
},
chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
chown: function(path, owner, group, dontResolveLastLink) {
@@ -849,12 +838,14 @@ LibraryManager.library = {
if (size == 0) {
___setErrNo(ERRNO_CODES.EINVAL);
return 0;
- } else if (size < FS.currentPath.length + 1) {
+ }
+ var cwd = FS.cwd();
+ if (size < cwd.length + 1) {
___setErrNo(ERRNO_CODES.ERANGE);
return 0;
} else {
- for (var i = 0; i < FS.currentPath.length; i++) {
- {{{ makeSetValue('buf', 'i', 'FS.currentPath.charCodeAt(i)', 'i8') }}}
+ for (var i = 0; i < cwd.length; i++) {
+ {{{ makeSetValue('buf', 'i', 'cwd.charCodeAt(i)', 'i8') }}}
}
{{{ makeSetValue('buf', 'i', '0', 'i8') }}}
return buf;
@@ -1600,12 +1591,6 @@ LibraryManager.library = {
__scanString.whiteSpace[{{{ charCode('\v') }}}] = 1;
__scanString.whiteSpace[{{{ charCode('\f') }}}] = 1;
__scanString.whiteSpace[{{{ charCode('\r') }}}] = 1;
- __scanString.whiteSpace[' '] = 1;
- __scanString.whiteSpace['\t'] = 1;
- __scanString.whiteSpace['\n'] = 1;
- __scanString.whiteSpace['\v'] = 1;
- __scanString.whiteSpace['\f'] = 1;
- __scanString.whiteSpace['\r'] = 1;
}
// Supports %x, %4x, %d.%d, %lld, %s, %f, %lf.
// TODO: Support all format specifiers.
@@ -1645,7 +1630,7 @@ LibraryManager.library = {
if (nextC > 0) {
var maxx = 1;
if (nextC > formatIndex+1) {
- var sub = format.substring(formatIndex+1, nextC)
+ var sub = format.substring(formatIndex+1, nextC);
maxx = parseInt(sub);
if (maxx != sub) maxx = 0;
}
@@ -1663,6 +1648,53 @@ LibraryManager.library = {
}
}
+ // handle %[...]
+ if (format[formatIndex] === '%' && format.indexOf('[', formatIndex+1) > 0) {
+ var match = /\%([0-9]*)\[(\^)?(\]?[^\]]*)\]/.exec(format.substring(formatIndex));
+ if (match) {
+ var maxNumCharacters = parseInt(match[1]) || Infinity;
+ var negateScanList = (match[2] === '^');
+ var scanList = match[3];
+
+ // expand "middle" dashs into character sets
+ var middleDashMatch;
+ while ((middleDashMatch = /([^\-])\-([^\-])/.exec(scanList))) {
+ var rangeStartCharCode = middleDashMatch[1].charCodeAt(0);
+ var rangeEndCharCode = middleDashMatch[2].charCodeAt(0);
+ for (var expanded = ''; rangeStartCharCode <= rangeEndCharCode; expanded += String.fromCharCode(rangeStartCharCode++));
+ scanList = scanList.replace(middleDashMatch[1] + '-' + middleDashMatch[2], expanded);
+ }
+
+ var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
+ argIndex += Runtime.getAlignSize('void*', null, true);
+ fields++;
+
+ for (var i = 0; i < maxNumCharacters; i++) {
+ next = get();
+ if (negateScanList) {
+ if (scanList.indexOf(String.fromCharCode(next)) < 0) {
+ {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}};
+ } else {
+ unget();
+ break;
+ }
+ } else {
+ if (scanList.indexOf(String.fromCharCode(next)) >= 0) {
+ {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}};
+ } else {
+ unget();
+ break;
+ }
+ }
+ }
+
+ // write out null-terminating character
+ {{{ makeSetValue('argPtr++', 0, '0', 'i8') }}};
+ formatIndex += match[0].length;
+
+ continue;
+ }
+ }
// remove whitespace
while (1) {
next = get();
@@ -1724,6 +1756,17 @@ LibraryManager.library = {
} else {
next = get();
var first = true;
+
+ // Strip the optional 0x prefix for %x.
+ if ((type == 'x' || type == 'X') && (next == {{{ charCode('0') }}})) {
+ var peek = get();
+ if (peek == {{{ charCode('x') }}} || peek == {{{ charCode('X') }}}) {
+ next = get();
+ } else {
+ unget();
+ }
+ }
+
while ((curr < max_ || isNaN(max_)) && next > 0) {
if (!(next in __scanString.whiteSpace) && // stop on whitespace
(type == 's' ||
@@ -1785,7 +1828,7 @@ LibraryManager.library = {
break;
}
fields++;
- } else if (format[formatIndex] in __scanString.whiteSpace) {
+ } else if (format[formatIndex].charCodeAt(0) in __scanString.whiteSpace) {
next = get();
while (next in __scanString.whiteSpace) {
if (next <= 0) break mainLoop; // End of input.
@@ -1856,6 +1899,7 @@ LibraryManager.library = {
var flagLeftAlign = false;
var flagAlternative = false;
var flagZeroPad = false;
+ var flagPadSign = false;
flagsLoop: while (1) {
switch (next) {
case {{{ charCode('+') }}}:
@@ -1874,6 +1918,9 @@ LibraryManager.library = {
flagZeroPad = true;
break;
}
+ case {{{ charCode(' ') }}}:
+ flagPadSign = true;
+ break;
default:
break flagsLoop;
}
@@ -2040,14 +2087,20 @@ LibraryManager.library = {
}
// Add sign if needed
- if (flagAlwaysSigned) {
- if (currArg < 0) {
- prefix = '-' + prefix;
- } else {
+ if (currArg >= 0) {
+ if (flagAlwaysSigned) {
prefix = '+' + prefix;
+ } else if (flagPadSign) {
+ prefix = ' ' + prefix;
}
}
+ // Move sign to prefix so we zero-pad after the sign
+ if (argText.charAt(0) == '-') {
+ prefix = '-' + prefix;
+ argText = argText.substr(1);
+ }
+
// Add padding.
while (prefix.length + argText.length < width) {
if (flagLeftAlign) {
@@ -2130,8 +2183,12 @@ LibraryManager.library = {
if (next == {{{ charCode('E') }}}) argText = argText.toUpperCase();
// Add sign.
- if (flagAlwaysSigned && currArg >= 0) {
- argText = '+' + argText;
+ if (currArg >= 0) {
+ if (flagAlwaysSigned) {
+ argText = '+' + argText;
+ } else if (flagPadSign) {
+ argText = ' ' + argText;
+ }
}
}
@@ -2769,6 +2826,13 @@ LibraryManager.library = {
asprintf: function(s, format, varargs) {
return _sprintf(-s, format, varargs);
},
+ dprintf__deps: ['_formatString', 'write'],
+ dprintf: function(fd, format, varargs) {
+ var result = __formatString(format, varargs);
+ var stack = Runtime.stackSave();
+ var ret = _write(fd, allocate(result, 'i8', ALLOC_STACK), result.length);
+ Runtime.stackRestore(stack);
+ },
#if TARGET_X86
// va_arg is just like our varargs
@@ -2777,6 +2841,7 @@ LibraryManager.library = {
vprintf: 'printf',
vsprintf: 'sprintf',
vasprintf: 'asprintf',
+ vdprintf: 'dprintf',
vscanf: 'scanf',
vfscanf: 'fscanf',
vsscanf: 'sscanf',
@@ -2804,6 +2869,10 @@ LibraryManager.library = {
vasprintf: function(s, format, va_arg) {
return _asprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}});
},
+ vdprintf__deps: ['dprintf'],
+ vdprintf: function (fd, format, va_arg) {
+ return _dprintf(fd, format, {{{ makeGetValue('va_arg', 0, '*') }}});
+ },
vscanf__deps: ['scanf'],
vscanf: function(format, va_arg) {
return _scanf(format, {{{ makeGetValue('va_arg', 0, '*') }}});
@@ -3674,6 +3743,7 @@ LibraryManager.library = {
},
// We always assume ASCII locale.
strcoll: 'strcmp',
+ strcoll_l: 'strcmp',
strcasecmp__asm: true,
strcasecmp__sig: 'iii',
@@ -3940,6 +4010,7 @@ LibraryManager.library = {
}
},
_toupper: 'toupper',
+ toupper_l: 'toupper',
tolower__asm: true,
tolower__sig: 'ii',
@@ -3950,54 +4021,65 @@ LibraryManager.library = {
return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0;
},
_tolower: 'tolower',
+ tolower_l: 'tolower',
// The following functions are defined as macros in glibc.
islower: function(chr) {
return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}};
},
+ islower_l: 'islower',
isupper: function(chr) {
return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}};
},
+ isupper_l: 'isupper',
isalpha: function(chr) {
return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
(chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
},
+ isalpha_l: 'isalpha',
isdigit: function(chr) {
return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}};
},
- isdigit_l: 'isdigit', // no locale support yet
+ isdigit_l: 'isdigit',
isxdigit: function(chr) {
return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
(chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) ||
(chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}});
},
- isxdigit_l: 'isxdigit', // no locale support yet
+ isxdigit_l: 'isxdigit',
isalnum: function(chr) {
return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
(chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
(chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
},
+ isalnum_l: 'isalnum',
ispunct: function(chr) {
return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) ||
(chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) ||
(chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) ||
(chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
},
+ ispunct_l: 'ispunct',
isspace: function(chr) {
return (chr == 32) || (chr >= 9 && chr <= 13);
},
+ isspace_l: 'isspace',
isblank: function(chr) {
return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
},
+ isblank_l: 'isblank',
iscntrl: function(chr) {
return (0 <= chr && chr <= 0x1F) || chr === 0x7F;
},
+ iscntrl_l: 'iscntrl',
isprint: function(chr) {
return 0x1F < chr && chr < 0x7F;
},
+ isprint_l: 'isprint',
isgraph: function(chr) {
return 0x20 < chr && chr < 0x7F;
},
+ isgraph_l: 'isgraph',
// Lookup tables for glibc ctype implementation.
__ctype_b_loc: function() {
// http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html
@@ -4098,7 +4180,7 @@ LibraryManager.library = {
llvm_va_end: function() {},
llvm_va_copy: function(ppdest, ppsrc) {
- // copy the list start
+ // copy the list start
{{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}};
// copy the list's current offset (will be advanced with each call to va_arg)
@@ -6270,11 +6352,15 @@ LibraryManager.library = {
// locale.h
// ==========================================================================
+ newlocale__deps: ['malloc'],
newlocale: function(mask, locale, base) {
- return 0;
+ return _malloc({{{ QUANTUM_SIZE}}});
},
- freelocale: function(locale) {},
+ freelocale__deps: ['free'],
+ freelocale: function(locale) {
+ _free(locale);
+ },
uselocale: function(locale) {
return 0;
@@ -8619,7 +8705,7 @@ function autoAddDeps(object, name) {
}
// Add aborting stubs for various libc stuff needed by libc++
-['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'swprintf', 'pthread_join', 'pthread_detach', 'strcoll_l', 'strxfrm_l', 'wcscoll_l', 'toupper_l', 'tolower_l', 'iswspace_l', 'iswprint_l', 'iswcntrl_l', 'iswupper_l', 'iswlower_l', 'iswalpha_l', 'iswdigit_l', 'iswpunct_l', 'iswxdigit_l', 'iswblank_l', 'wcsxfrm_l', 'towupper_l', 'towlower_l', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) {
+['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'pthread_join', 'pthread_detach', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) {
LibraryManager.library[aborter] = function() { throw 'TODO: ' + aborter };
});
diff --git a/src/library_fs.js b/src/library_fs.js
index 8bf0a83a..c4b29227 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -1,5 +1,5 @@
mergeInto(LibraryManager.library, {
- $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'],
+ $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'],
$FS__postset: 'FS.staticInit();' +
'__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' +
'__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' +
@@ -14,6 +14,7 @@ mergeInto(LibraryManager.library, {
'Module["FS_createDevice"] = FS.createDevice;',
$FS: {
root: null,
+ mounts: [],
devices: [null],
streams: [null],
nextInode: 1,
@@ -50,11 +51,8 @@ mergeInto(LibraryManager.library, {
//
// paths
//
- cwd: function() {
- return FS.currentPath;
- },
lookupPath: function(path, opts) {
- path = PATH.resolve(FS.currentPath, path);
+ path = PATH.resolve(FS.cwd(), path);
opts = opts || { recurse_count: 0 };
if (opts.recurse_count > 8) { // max recursive lookup of 8
@@ -309,7 +307,7 @@ mergeInto(LibraryManager.library, {
if (!FS.isDir(node.mode)) {
return ERRNO_CODES.ENOTDIR;
}
- if (FS.isRoot(node) || FS.getPath(node) === FS.currentPath) {
+ if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) {
return ERRNO_CODES.EBUSY;
}
} else {
@@ -422,17 +420,45 @@ mergeInto(LibraryManager.library, {
//
// core
//
+ syncfs: function(populate, callback) {
+ if (typeof(populate) === 'function') {
+ callback = populate;
+ populate = false;
+ }
+
+ var completed = 0;
+ var total = FS.mounts.length;
+ var done = function(err) {
+ if (err) {
+ return callback(err);
+ }
+ if (++completed >= total) {
+ callback(null);
+ }
+ };
+
+ // sync all mounts
+ for (var i = 0; i < FS.mounts.length; i++) {
+ var mount = FS.mounts[i];
+ if (!mount.type.syncfs) {
+ done(null);
+ continue;
+ }
+ mount.type.syncfs(mount, populate, done);
+ }
+ },
mount: function(type, opts, mountpoint) {
+ var lookup;
+ if (mountpoint) {
+ lookup = FS.lookupPath(mountpoint, { follow: false });
+ mountpoint = lookup.path; // use the absolute path
+ }
var mount = {
type: type,
opts: opts,
mountpoint: mountpoint,
root: null
};
- var lookup;
- if (mountpoint) {
- lookup = FS.lookupPath(mountpoint, { follow: false });
- }
// create a root node for the fs
var root = type.mount(mount);
root.mount = mount;
@@ -446,6 +472,8 @@ mergeInto(LibraryManager.library, {
FS.root = mount.root;
}
}
+ // add to our cached list of mounts
+ FS.mounts.push(mount);
return root;
},
lookup: function(parent, name) {
@@ -759,7 +787,6 @@ mergeInto(LibraryManager.library, {
follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}})
});
node = lookup.node;
- path = lookup.path;
} catch (e) {
// ignore
}
@@ -791,10 +818,13 @@ mergeInto(LibraryManager.library, {
if ((flags & {{{ cDefine('O_TRUNC')}}})) {
FS.truncate(node, 0);
}
+ // we've already handled these, don't pass down to the underlying vfs
+ flags &= ~({{{ cDefine('O_EXCL') }}} | {{{ cDefine('O_TRUNC') }}});
+
// register the stream with the filesystem
var stream = FS.createStream({
- path: path,
node: node,
+ path: FS.getPath(node), // we want the absolute path to the node
flags: flags,
seekable: true,
position: 0,
@@ -959,8 +989,21 @@ mergeInto(LibraryManager.library, {
//
// module-level FS code
- // TODO move to pre/postamble
//
+ cwd: function() {
+ return FS.currentPath;
+ },
+ chdir: function(path) {
+ var lookup = FS.lookupPath(path, { follow: true });
+ if (!FS.isDir(lookup.node.mode)) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR);
+ }
+ var err = FS.nodePermissions(lookup.node, 'x');
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ FS.currentPath = lookup.path;
+ },
createDefaultDirectories: function() {
FS.mkdir('/tmp');
},
@@ -1367,7 +1410,10 @@ mergeInto(LibraryManager.library, {
throw new FS.ErrnoError(ERRNO_CODES.EIO);
}
var contents = stream.node.contents;
+ if (position >= contents.length)
+ return 0;
var size = Math.min(contents.length - position, length);
+ assert(size >= 0);
if (contents.slice) { // normal array
for (var i = 0; i < size; i++) {
buffer[offset + i] = contents[position + i];
diff --git a/src/library_gl.js b/src/library_gl.js
index 16ea5531..83e68777 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -217,6 +217,23 @@ var LibraryGL = {
throw 'Invalid format (' + format + ')';
}
break;
+ case 0x1403 /* GL_UNSIGNED_SHORT */:
+ if (format == 0x1902 /* GL_DEPTH_COMPONENT */) {
+ sizePerPixel = 2;
+ } else {
+ throw 'Invalid format (' + format + ')';
+ }
+ break;
+ case 0x1405 /* GL_UNSIGNED_INT */:
+ if (format == 0x1902 /* GL_DEPTH_COMPONENT */) {
+ sizePerPixel = 4;
+ } else {
+ throw 'Invalid format (' + format + ')';
+ }
+ break;
+ case 0x84FA /* UNSIGNED_INT_24_8_WEBGL */:
+ sizePerPixel = 4;
+ break;
case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */:
case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */:
case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */:
@@ -244,6 +261,8 @@ var LibraryGL = {
pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}};
} else if (type == 0x1406 /* GL_FLOAT */) {
pixels = {{{ makeHEAPView('F32', 'pixels', 'pixels+bytes') }}};
+ } else if (type == 0x1405 /* GL_UNSIGNED_INT */ || type == 0x84FA /* UNSIGNED_INT_24_8_WEBGL */) {
+ pixels = {{{ makeHEAPView('U32', 'pixels', 'pixels+bytes') }}};
} else {
pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+bytes') }}};
}
@@ -291,6 +310,9 @@ var LibraryGL = {
Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER,
0,
HEAPU8.subarray(cb.ptr, cb.ptr + size));
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(cb.size, cb.type, cb.stride, 0);
+#endif
Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0);
}
},
@@ -302,6 +324,56 @@ var LibraryGL = {
},
#endif
+#if GL_ASSERTIONS
+ validateGLObjectID: function(objectHandleArray, objectID, callerFunctionName, objectReadableType) {
+ if (objectID != 0) {
+ if (objectHandleArray[objectID] === null) {
+ console.error(callerFunctionName + ' called with an already deleted ' + objectReadableType + ' ID ' + objectID + '!');
+ } else if (!objectHandleArray[objectID]) {
+ console.error(callerFunctionName + ' called with an invalid ' + objectReadableType + ' ID ' + objectID + '!');
+ }
+ }
+ },
+ // Validates that user obeys GL spec #6.4: http://www.khronos.org/registry/webgl/specs/latest/1.0/#6.4
+ validateVertexAttribPointer: function(dimension, dataType, stride, offset) {
+ var sizeBytes = 1;
+ switch(dataType) {
+ case 0x1400 /* GL_BYTE */:
+ case 0x1401 /* GL_UNSIGNED_BYTE */:
+ sizeBytes = 1;
+ break;
+ case 0x1402 /* GL_SHORT */:
+ case 0x1403 /* GL_UNSIGNED_SHORT */:
+ sizeBytes = 2;
+ break;
+ case 0x1404 /* GL_INT */:
+ case 0x1405 /* GL_UNSIGNED_INT */:
+ case 0x1406 /* GL_FLOAT */:
+ sizeBytes = 4;
+ break;
+ case 0x140A /* GL_DOUBLE */:
+ sizeBytes = 8;
+ break;
+ default:
+ console.error('Invalid vertex attribute data type GLenum ' + dataType + ' passed to GL function!');
+ }
+ if (dimension == 0x80E1 /* GL_BGRA */) {
+ console.error('WebGL does not support size=GL_BGRA in a call to glVertexAttribPointer! Please use size=4 and type=GL_UNSIGNED_BYTE instead!');
+ } else if (dimension < 1 || dimension > 4) {
+ console.error('Invalid dimension='+dimension+' in call to glVertexAttribPointer, must be 1,2,3 or 4.');
+ }
+ if (stride < 0 || stride > 255) {
+ console.error('Invalid stride='+stride+' in call to glVertexAttribPointer. Note that maximum supported stride in WebGL is 255!');
+ }
+ if (offset % sizeBytes != 0) {
+ console.error('GL spec section 6.4 error: vertex attribute data offset of ' + offset + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!');
+ }
+ if (stride % sizeBytes != 0) {
+ console.error('GL spec section 6.4 error: vertex attribute data stride of ' + stride + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!');
+ }
+ },
+#endif
+
initExtensions: function() {
if (GL.initExtensions.done) return;
GL.initExtensions.done = true;
@@ -334,6 +406,10 @@ var LibraryGL = {
GL.elementIndexUintExt = Module.ctx.getExtension('OES_element_index_uint');
GL.standardDerivativesExt = Module.ctx.getExtension('OES_standard_derivatives');
+
+ GL.depthTextureExt = Module.ctx.getExtension("WEBGL_depth_texture") ||
+ Module.ctx.getExtension("MOZ_WEBGL_depth_texture") ||
+ Module.ctx.getExtension("WEBKIT_WEBGL_depth_texture");
}
},
@@ -588,6 +664,9 @@ var LibraryGL = {
glBindTexture__sig: 'vii',
glBindTexture: function(target, texture) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.textures, texture, 'glBindTexture', 'texture');
+#endif
Module.ctx.bindTexture(target, texture ? GL.textures[texture] : null);
},
@@ -710,6 +789,9 @@ var LibraryGL = {
glBindRenderbuffer__sig: 'vii',
glBindRenderbuffer: function(target, renderbuffer) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glBindRenderbuffer', 'renderbuffer');
+#endif
Module.ctx.bindRenderbuffer(target, renderbuffer ? GL.renderbuffers[renderbuffer] : null);
},
@@ -727,6 +809,9 @@ var LibraryGL = {
glGetUniformfv__sig: 'viii',
glGetUniformfv: function(program, location, params) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetUniformfv', 'program');
+#endif
var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]);
if (typeof data == 'number') {
{{{ makeSetValue('params', '0', 'data', 'float') }}};
@@ -739,6 +824,9 @@ var LibraryGL = {
glGetUniformiv__sig: 'viii',
glGetUniformiv: function(program, location, params) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetUniformiv', 'program');
+#endif
var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]);
if (typeof data == 'number' || typeof data == 'boolean') {
{{{ makeSetValue('params', '0', 'data', 'i32') }}};
@@ -751,6 +839,9 @@ var LibraryGL = {
glGetUniformLocation__sig: 'iii',
glGetUniformLocation: function(program, name) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetUniformLocation', 'program');
+#endif
name = Pointer_stringify(name);
var ptable = GL.uniformTable[program];
if (!ptable) ptable = GL.uniformTable[program] = {};
@@ -810,6 +901,9 @@ var LibraryGL = {
glGetActiveUniform__sig: 'viiiiiii',
glGetActiveUniform: function(program, index, bufSize, length, size, type, name) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetActiveUniform', 'program');
+#endif
program = GL.programs[program];
var info = Module.ctx.getActiveUniform(program, index);
@@ -1018,6 +1112,9 @@ var LibraryGL = {
glBindBuffer__sig: 'vii',
glBindBuffer: function(target, buffer) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.buffers, buffer, 'glBindBuffer', 'buffer');
+#endif
var bufferObj = buffer ? GL.buffers[buffer] : null;
if (target == Module.ctx.ARRAY_BUFFER) {
@@ -1062,6 +1159,9 @@ var LibraryGL = {
glGetActiveAttrib__sig: 'viiiiiii',
glGetActiveAttrib: function(program, index, bufSize, length, size, type, name) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetActiveAttrib', 'program');
+#endif
program = GL.programs[program];
var info = Module.ctx.getActiveAttrib(program, index);
@@ -1094,6 +1194,9 @@ var LibraryGL = {
glGetAttachedShaders__sig: 'viiii',
glGetAttachedShaders: function(program, maxCount, count, shaders) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetAttachedShaders', 'program');
+#endif
var result = Module.ctx.getAttachedShaders(GL.programs[program]);
var len = result.length;
if (len > maxCount) {
@@ -1109,12 +1212,18 @@ var LibraryGL = {
glShaderSource__sig: 'viiii',
glShaderSource: function(shader, count, string, length) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.shaders, shader, 'glShaderSource', 'shader');
+#endif
var source = GL.getSource(shader, count, string, length);
Module.ctx.shaderSource(GL.shaders[shader], source);
},
glGetShaderSource__sig: 'viiii',
glGetShaderSource: function(shader, bufSize, length, source) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderSource', 'shader');
+#endif
var result = Module.ctx.getShaderSource(GL.shaders[shader]);
result = result.slice(0, Math.max(0, bufSize - 1));
writeStringToMemory(result, source);
@@ -1125,11 +1234,17 @@ var LibraryGL = {
glCompileShader__sig: 'vi',
glCompileShader: function(shader) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.shaders, shader, 'glCompileShader', 'shader');
+#endif
Module.ctx.compileShader(GL.shaders[shader]);
},
glGetShaderInfoLog__sig: 'viiii',
glGetShaderInfoLog: function(shader, maxLength, length, infoLog) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderInfoLog', 'shader');
+#endif
var log = Module.ctx.getShaderInfoLog(GL.shaders[shader]);
// Work around a bug in Chromium which causes getShaderInfoLog to return null
if (!log) {
@@ -1144,6 +1259,9 @@ var LibraryGL = {
glGetShaderiv__sig: 'viii',
glGetShaderiv : function(shader, pname, p) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderiv', 'shader');
+#endif
if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH
{{{ makeSetValue('p', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[shader]).length + 1', 'i32') }}};
} else {
@@ -1153,6 +1271,9 @@ var LibraryGL = {
glGetProgramiv__sig: 'viii',
glGetProgramiv : function(program, pname, p) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetProgramiv', 'program');
+#endif
if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH
{{{ makeSetValue('p', '0', 'Module.ctx.getProgramInfoLog(GL.programs[program]).length + 1', 'i32') }}};
} else {
@@ -1187,12 +1308,20 @@ var LibraryGL = {
glAttachShader__sig: 'vii',
glAttachShader: function(program, shader) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glAttachShader', 'program');
+ GL.validateGLObjectID(GL.shaders, shader, 'glAttachShader', 'shader');
+#endif
Module.ctx.attachShader(GL.programs[program],
GL.shaders[shader]);
},
glDetachShader__sig: 'vii',
glDetachShader: function(program, shader) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glDetachShader', 'program');
+ GL.validateGLObjectID(GL.shaders, shader, 'glDetachShader', 'shader');
+#endif
Module.ctx.detachShader(GL.programs[program],
GL.shaders[shader]);
},
@@ -1206,12 +1335,18 @@ var LibraryGL = {
glLinkProgram__sig: 'vi',
glLinkProgram: function(program) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glLinkProgram', 'program');
+#endif
Module.ctx.linkProgram(GL.programs[program]);
GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking
},
glGetProgramInfoLog__sig: 'viiii',
glGetProgramInfoLog: function(program, maxLength, length, infoLog) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glGetProgramInfoLog', 'program');
+#endif
var log = Module.ctx.getProgramInfoLog(GL.programs[program]);
// Work around a bug in Chromium which causes getProgramInfoLog to return null
if (!log) {
@@ -1226,11 +1361,17 @@ var LibraryGL = {
glUseProgram__sig: 'vi',
glUseProgram: function(program) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glUseProgram', 'program');
+#endif
Module.ctx.useProgram(program ? GL.programs[program] : null);
},
glValidateProgram__sig: 'vi',
glValidateProgram: function(program) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glValidateProgram', 'program');
+#endif
Module.ctx.validateProgram(GL.programs[program]);
},
@@ -1243,12 +1384,18 @@ var LibraryGL = {
glBindAttribLocation__sig: 'viii',
glBindAttribLocation: function(program, index, name) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'glBindAttribLocation', 'program');
+#endif
name = Pointer_stringify(name);
Module.ctx.bindAttribLocation(GL.programs[program], index, name);
},
glBindFramebuffer__sig: 'vii',
glBindFramebuffer: function(target, framebuffer) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.framebuffers, framebuffer, 'glBindFramebuffer', 'framebuffer');
+#endif
Module.ctx.bindFramebuffer(target, framebuffer ? GL.framebuffers[framebuffer] : null);
},
@@ -1276,12 +1423,18 @@ var LibraryGL = {
glFramebufferRenderbuffer__sig: 'viiii',
glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glFramebufferRenderbuffer', 'renderbuffer');
+#endif
Module.ctx.framebufferRenderbuffer(target, attachment, renderbuffertarget,
GL.renderbuffers[renderbuffer]);
},
glFramebufferTexture2D__sig: 'viiiii',
glFramebufferTexture2D: function(target, attachment, textarget, texture, level) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.textures, texture, 'glFramebufferTexture2D', 'texture');
+#endif
Module.ctx.framebufferTexture2D(target, attachment, textarget,
GL.textures[texture], level);
},
@@ -3161,6 +3314,9 @@ var LibraryGL = {
var clientAttributes = GL.immediate.clientAttributes;
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(positionSize, positionType, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset);
+#endif
Module.ctx.vertexAttribPointer(this.positionLocation, positionSize, positionType, false,
GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset);
Module.ctx.enableVertexAttribArray(this.positionLocation);
@@ -3173,6 +3329,9 @@ var LibraryGL = {
if (attribLoc === undefined || attribLoc < 0) continue;
if (texUnitID < textureSizes.length && textureSizes[texUnitID]) {
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(textureSizes[texUnitID], textureTypes[texUnitID], GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset);
+#endif
Module.ctx.vertexAttribPointer(attribLoc, textureSizes[texUnitID], textureTypes[texUnitID], false,
GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset);
Module.ctx.enableVertexAttribArray(attribLoc);
@@ -3189,6 +3348,9 @@ var LibraryGL = {
}
}
if (colorSize) {
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(colorSize, colorType, GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset);
+#endif
Module.ctx.vertexAttribPointer(this.colorLocation, colorSize, colorType, true,
GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset);
Module.ctx.enableVertexAttribArray(this.colorLocation);
@@ -3197,6 +3359,9 @@ var LibraryGL = {
Module.ctx.vertexAttrib4fv(this.colorLocation, GL.immediate.clientColor);
}
if (this.hasNormal) {
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(normalSize, normalType, GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset);
+#endif
Module.ctx.vertexAttribPointer(this.normalLocation, normalSize, normalType, true,
GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset);
Module.ctx.enableVertexAttribArray(this.normalLocation);
@@ -4176,6 +4341,9 @@ var LibraryGL = {
}
cb.clientside = false;
#endif
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(size, type, stride, ptr);
+#endif
Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr);
},
diff --git a/src/library_idbfs.js b/src/library_idbfs.js
new file mode 100644
index 00000000..9031bad8
--- /dev/null
+++ b/src/library_idbfs.js
@@ -0,0 +1,216 @@
+mergeInto(LibraryManager.library, {
+ $IDBFS__deps: ['$FS', '$MEMFS', '$PATH'],
+ $IDBFS: {
+ dbs: {},
+ indexedDB: function() {
+ return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+ },
+ DB_VERSION: 20,
+ DB_STORE_NAME: 'FILE_DATA',
+ // reuse all of the core MEMFS functionality
+ mount: function(mount) {
+ return MEMFS.mount.apply(null, arguments);
+ },
+ // the only custom function IDBFS implements is to handle
+ // synchronizing the wrapped MEMFS with a backing IDB instance
+ syncfs: function(mount, populate, callback) {
+ IDBFS.getLocalSet(mount, function(err, local) {
+ if (err) return callback(err);
+
+ IDBFS.getRemoteSet(mount, function(err, remote) {
+ if (err) return callback(err);
+
+ var src = populate ? remote : local;
+ var dst = populate ? local : remote;
+
+ IDBFS.reconcile(src, dst, callback);
+ });
+ });
+ },
+ reconcile: function(src, dst, callback) {
+ var total = 0;
+
+ var create = {};
+ for (var key in src.files) {
+ if (!src.files.hasOwnProperty(key)) continue;
+ var e = src.files[key];
+ var e2 = dst.files[key];
+ if (!e2 || e.timestamp > e2.timestamp) {
+ create[key] = e;
+ total++;
+ }
+ }
+
+ var remove = {};
+ for (var key in dst.files) {
+ if (!dst.files.hasOwnProperty(key)) continue;
+ var e = dst.files[key];
+ var e2 = src.files[key];
+ if (!e2) {
+ remove[key] = e;
+ total++;
+ }
+ }
+
+ if (!total) {
+ // early out
+ return callback(null);
+ }
+
+ var completed = 0;
+ var done = function(err) {
+ if (err) return callback(err);
+ if (++completed >= total) {
+ return callback(null);
+ }
+ };
+
+ // create a single transaction to handle and IDB reads / writes we'll need to do
+ var db = src.type === 'remote' ? src.db : dst.db;
+ var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite');
+ transaction.onerror = function() { callback(this.error); };
+ var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
+
+ for (var path in create) {
+ if (!create.hasOwnProperty(path)) continue;
+ var entry = create[path];
+
+ if (dst.type === 'local') {
+ // save file to local
+ try {
+ if (FS.isDir(entry.mode)) {
+ FS.mkdir(path, entry.mode);
+ } else if (FS.isFile(entry.mode)) {
+ var stream = FS.open(path, 'w+', 0666);
+ FS.write(stream, entry.contents, 0, entry.contents.length, 0, true /* canOwn */);
+ FS.close(stream);
+ }
+ done(null);
+ } catch (e) {
+ return done(e);
+ }
+ } else {
+ // save file to IDB
+ var req = store.put(entry, path);
+ req.onsuccess = function() { done(null); };
+ req.onerror = function() { done(this.error); };
+ }
+ }
+
+ for (var path in remove) {
+ if (!remove.hasOwnProperty(path)) continue;
+ var entry = remove[path];
+
+ if (dst.type === 'local') {
+ // delete file from local
+ try {
+ if (FS.isDir(entry.mode)) {
+ // TODO recursive delete?
+ FS.rmdir(path);
+ } else if (FS.isFile(entry.mode)) {
+ FS.unlink(path);
+ }
+ done(null);
+ } catch (e) {
+ return done(e);
+ }
+ } else {
+ // delete file from IDB
+ var req = store.delete(path);
+ req.onsuccess = function() { done(null); };
+ req.onerror = function() { done(this.error); };
+ }
+ }
+ },
+ getLocalSet: function(mount, callback) {
+ var files = {};
+
+ var isRealDir = function(p) {
+ return p !== '.' && p !== '..';
+ };
+ var toAbsolute = function(root) {
+ return function(p) {
+ return PATH.join(root, p);
+ }
+ };
+
+ var check = FS.readdir(mount.mountpoint)
+ .filter(isRealDir)
+ .map(toAbsolute(mount.mountpoint));
+
+ while (check.length) {
+ var path = check.pop();
+ var stat, node;
+
+ try {
+ var lookup = FS.lookupPath(path);
+ node = lookup.node;
+ stat = FS.stat(path);
+ } catch (e) {
+ return callback(e);
+ }
+
+ if (FS.isDir(stat.mode)) {
+ check.push.apply(check, FS.readdir(path)
+ .filter(isRealDir)
+ .map(toAbsolute(path)));
+
+ files[path] = { mode: stat.mode, timestamp: stat.mtime };
+ } else if (FS.isFile(stat.mode)) {
+ files[path] = { contents: node.contents, mode: stat.mode, timestamp: stat.mtime };
+ } else {
+ return callback(new Error('node type not supported'));
+ }
+ }
+
+ return callback(null, { type: 'local', files: files });
+ },
+ getDB: function(name, callback) {
+ // look it up in the cache
+ var db = IDBFS.dbs[name];
+ if (db) {
+ return callback(null, db);
+ }
+ var req;
+ try {
+ req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION);
+ } catch (e) {
+ return onerror(e);
+ }
+ req.onupgradeneeded = function() {
+ db = req.result;
+ db.createObjectStore(IDBFS.DB_STORE_NAME);
+ };
+ req.onsuccess = function() {
+ db = req.result;
+ // add to the cache
+ IDBFS.dbs[name] = db;
+ callback(null, db);
+ };
+ req.onerror = function() {
+ callback(this.error);
+ };
+ },
+ getRemoteSet: function(mount, callback) {
+ var files = {};
+
+ IDBFS.getDB(mount.mountpoint, function(err, db) {
+ if (err) return callback(err);
+
+ var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly');
+ transaction.onerror = function() { callback(this.error); };
+
+ var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
+ store.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (!cursor) {
+ return callback(null, { type: 'remote', db: db, files: files });
+ }
+
+ files[cursor.key] = cursor.value;
+ cursor.continue();
+ };
+ });
+ }
+ }
+});
diff --git a/src/library_memfs.js b/src/library_memfs.js
index 354f5e95..4e56d996 100644
--- a/src/library_memfs.js
+++ b/src/library_memfs.js
@@ -5,18 +5,10 @@ mergeInto(LibraryManager.library, {
CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary)
CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink
CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array
- ensureFlexible: function(node) {
- if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) {
- var contents = node.contents;
- node.contents = Array.prototype.slice.call(contents);
- node.contentMode = MEMFS.CONTENT_FLEXIBLE;
- }
- },
-
mount: function(mount) {
- return MEMFS.create_node(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
+ return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
},
- create_node: function(parent, name, mode, dev) {
+ createNode: function(parent, name, mode, dev) {
if (FS.isBlkdev(mode) || FS.isFIFO(mode)) {
// no supported
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
@@ -74,6 +66,13 @@ mergeInto(LibraryManager.library, {
}
return node;
},
+ ensureFlexible: function(node) {
+ if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) {
+ var contents = node.contents;
+ node.contents = Array.prototype.slice.call(contents);
+ node.contentMode = MEMFS.CONTENT_FLEXIBLE;
+ }
+ },
node_ops: {
getattr: function(node) {
var attr = {};
@@ -121,7 +120,7 @@ mergeInto(LibraryManager.library, {
throw new FS.ErrnoError(ERRNO_CODES.ENOENT);
},
mknod: function(parent, name, mode, dev) {
- return MEMFS.create_node(parent, name, mode, dev);
+ return MEMFS.createNode(parent, name, mode, dev);
},
rename: function(old_node, new_dir, new_name) {
// if we're overwriting a directory at new_name, make sure it's empty.
@@ -163,7 +162,7 @@ mergeInto(LibraryManager.library, {
return entries;
},
symlink: function(parent, newname, oldpath) {
- var node = MEMFS.create_node(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0);
+ var node = MEMFS.createNode(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0);
node.link = oldpath;
return node;
},
@@ -177,7 +176,10 @@ mergeInto(LibraryManager.library, {
stream_ops: {
read: function(stream, buffer, offset, length, position) {
var contents = stream.node.contents;
+ if (position >= contents.length)
+ return 0;
var size = Math.min(contents.length - position, length);
+ assert(size >= 0);
#if USE_TYPED_ARRAYS == 2
if (size > 8 && contents.subarray) { // non-trivial, and typed array
buffer.set(contents.subarray(position, position + size), offset);
diff --git a/src/library_nodefs.js b/src/library_nodefs.js
new file mode 100644
index 00000000..d8df1689
--- /dev/null
+++ b/src/library_nodefs.js
@@ -0,0 +1,234 @@
+mergeInto(LibraryManager.library, {
+ $NODEFS__deps: ['$FS', '$PATH'],
+ $NODEFS__postset: 'if (ENVIRONMENT_IS_NODE) { var fs = require("fs"); }',
+ $NODEFS: {
+ mount: function (mount) {
+ assert(ENVIRONMENT_IS_NODE);
+ return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0);
+ },
+ createNode: function (parent, name, mode, dev) {
+ if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
+ }
+ var node = FS.createNode(parent, name, mode);
+ node.node_ops = NODEFS.node_ops;
+ node.stream_ops = NODEFS.stream_ops;
+ return node;
+ },
+ getMode: function (path) {
+ var stat;
+ try {
+ stat = fs.lstatSync(path);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ return stat.mode;
+ },
+ realPath: function (node) {
+ var parts = [];
+ while (node.parent !== node) {
+ parts.push(node.name);
+ node = node.parent;
+ }
+ parts.push(node.mount.opts.root);
+ parts.reverse();
+ return PATH.join.apply(null, parts);
+ },
+ node_ops: {
+ getattr: function(node) {
+ var path = NODEFS.realPath(node);
+ var stat;
+ try {
+ stat = fs.lstatSync(path);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ return {
+ dev: stat.dev,
+ ino: stat.ino,
+ mode: stat.mode,
+ nlink: stat.nlink,
+ uid: stat.uid,
+ gid: stat.gid,
+ rdev: stat.rdev,
+ size: stat.size,
+ atime: stat.atime,
+ mtime: stat.mtime,
+ ctime: stat.ctime,
+ blksize: stat.blksize,
+ blocks: stat.blocks
+ };
+ },
+ setattr: function(node, attr) {
+ var path = NODEFS.realPath(node);
+ try {
+ if (attr.mode !== undefined) {
+ fs.chmodSync(path, attr.mode);
+ // update the common node structure mode as well
+ node.mode = attr.mode;
+ }
+ if (attr.timestamp !== undefined) {
+ var date = new Date(attr.timestamp);
+ fs.utimesSync(path, date, date);
+ }
+ if (attr.size !== undefined) {
+ fs.truncateSync(path, attr.size);
+ }
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ lookup: function (parent, name) {
+ var path = PATH.join(NODEFS.realPath(parent), name);
+ var mode = NODEFS.getMode(path);
+ return NODEFS.createNode(parent, name, mode);
+ },
+ mknod: function (parent, name, mode, dev) {
+ var node = NODEFS.createNode(parent, name, mode, dev);
+ // create the backing node for this in the fs root as well
+ var path = NODEFS.realPath(node);
+ try {
+ if (FS.isDir(node.mode)) {
+ fs.mkdirSync(path, node.mode);
+ } else {
+ fs.writeFileSync(path, '', { mode: node.mode });
+ }
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ return node;
+ },
+ rename: function (oldNode, newDir, newName) {
+ var oldPath = NODEFS.realPath(oldNode);
+ var newPath = PATH.join(NODEFS.realPath(newDir), newName);
+ try {
+ fs.renameSync(oldPath, newPath);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ unlink: function(parent, name) {
+ var path = PATH.join(NODEFS.realPath(parent), name);
+ try {
+ fs.unlinkSync(path);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ rmdir: function(parent, name) {
+ var path = PATH.join(NODEFS.realPath(parent), name);
+ try {
+ fs.rmdirSync(path);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ readdir: function(node) {
+ var path = NODEFS.realPath(node);
+ try {
+ return fs.readdirSync(path);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ symlink: function(parent, newName, oldPath) {
+ var newPath = PATH.join(NODEFS.realPath(parent), newName);
+ try {
+ fs.symlinkSync(oldPath, newPath);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ readlink: function(node) {
+ var path = NODEFS.realPath(node);
+ try {
+ return fs.readlinkSync(path);
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ },
+ stream_ops: {
+ open: function (stream) {
+ var path = NODEFS.realPath(stream.node);
+ try {
+ if (FS.isFile(stream.node.mode)) {
+ stream.nfd = fs.openSync(path, stream.flags);
+ }
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ close: function (stream) {
+ try {
+ if (FS.isFile(stream.node.mode)) {
+ fs.closeSync(stream.nfd);
+ }
+ } catch (e) {
+ if (!e.code) throw e;
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ },
+ read: function (stream, buffer, offset, length, position) {
+ // FIXME this is terrible.
+ var nbuffer = new Buffer(length);
+ var res;
+ try {
+ res = fs.readSync(stream.nfd, nbuffer, 0, length, position);
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ if (res > 0) {
+ for (var i = 0; i < res; i++) {
+ buffer[offset + i] = nbuffer[i];
+ }
+ }
+ return res;
+ },
+ write: function (stream, buffer, offset, length, position) {
+ // FIXME this is terrible.
+ var nbuffer = new Buffer(buffer.subarray(offset, offset + length));
+ var res;
+ try {
+ res = fs.writeSync(stream.nfd, nbuffer, 0, length, position);
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ return res;
+ },
+ llseek: function (stream, offset, whence) {
+ var position = offset;
+ if (whence === 1) { // SEEK_CUR.
+ position += stream.position;
+ } else if (whence === 2) { // SEEK_END.
+ if (FS.isFile(stream.node.mode)) {
+ try {
+ var stat = fs.fstatSync(stream.nfd);
+ position += stat.size;
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES[e.code]);
+ }
+ }
+ }
+
+ if (position < 0) {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
+ }
+
+ stream.position = position;
+ return position;
+ }
+ }
+ }
+}); \ No newline at end of file
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 656b5a02..1d6a00b6 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -627,7 +627,7 @@ var LibrarySDL = {
// since the browser engine handles that for us. Therefore, in JS we just
// maintain a list of channels and return IDs for them to the SDL consumer.
allocateChannels: function(num) { // called from Mix_AllocateChannels and init
- if (SDL.numChannels && SDL.numChannels >= num) return;
+ if (SDL.numChannels && SDL.numChannels >= num && num != 0) return;
SDL.numChannels = num;
SDL.channels = [];
for (var i = 0; i < num; i++) {
@@ -1054,7 +1054,10 @@ var LibrarySDL = {
} else {
dr = { x: 0, y: 0, w: -1, h: -1 };
}
+ var oldAlpha = dstData.ctx.globalAlpha;
+ dstData.ctx.globalAlpha = srcData.alpha/255;
dstData.ctx.drawImage(srcData.canvas, sr.x, sr.y, sr.w, sr.h, dr.x, dr.y, sr.w, sr.h);
+ dstData.ctx.globalAlpha = oldAlpha;
if (dst != SDL.screen) {
// XXX As in IMG_Load, for compatibility we write out |pixels|
console.log('WARNING: copying canvas data to memory for compatibility');
@@ -1377,60 +1380,240 @@ var LibrarySDL = {
// SDL_Audio
- // TODO fix SDL_OpenAudio, and add some tests for it. It's currently broken.
SDL_OpenAudio: function(desired, obtained) {
- SDL.allocateChannels(32);
-
- SDL.audio = {
- freq: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.freq, 'i32', 0, 1) }}},
- format: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.format, 'i16', 0, 1) }}},
- channels: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.channels, 'i8', 0, 1) }}},
- samples: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.samples, 'i16', 0, 1) }}},
- callback: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.callback, 'void*', 0, 1) }}},
- userdata: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.userdata, 'void*', 0, 1) }}},
- paused: true,
- timer: null
- };
-
- if (obtained) {
- {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.freq, 'SDL.audio.freq', 'i32') }}}; // no good way for us to know if the browser can really handle this
- {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.format, 33040, 'i16') }}}; // float, signed, 16-bit
- {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.channels, 'SDL.audio.channels', 'i8') }}};
- {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.silence, makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.silence, 'i8', 0, 1), 'i8') }}}; // unclear if browsers can provide this
- {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.samples, 'SDL.audio.samples', 'i16') }}};
- {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.callback, 'SDL.audio.callback', '*') }}};
- {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.userdata, 'SDL.audio.userdata', '*') }}};
- }
-
- var totalSamples = SDL.audio.samples*SDL.audio.channels;
- SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio
- SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
- SDL.audio.caller = function() {
- Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]);
- SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize);
- };
- // Mozilla Audio API. TODO: Other audio APIs
try {
- SDL.audio.mozOutput = new Audio();
- SDL.audio.mozOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler
- SDL.audio.mozBuffer = new Float32Array(totalSamples);
- SDL.audio.pushAudio = function(ptr, size) {
- var mozBuffer = SDL.audio.mozBuffer;
- for (var i = 0; i < totalSamples; i++) {
- mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?)
+ SDL.audio = {
+ freq: {{{ makeGetValue('desired', 'C_STRUCTS.SDL_AudioSpec.freq', 'i32', 0, 1) }}},
+ format: {{{ makeGetValue('desired', 'C_STRUCTS.SDL_AudioSpec.format', 'i16', 0, 1) }}},
+ channels: {{{ makeGetValue('desired', 'C_STRUCTS.SDL_AudioSpec.channels', 'i8', 0, 1) }}},
+ samples: {{{ makeGetValue('desired', 'C_STRUCTS.SDL_AudioSpec.samples', 'i16', 0, 1) }}}, // Samples in the CB buffer per single sound channel.
+ callback: {{{ makeGetValue('desired', 'C_STRUCTS.SDL_AudioSpec.callback', 'void*', 0, 1) }}},
+ userdata: {{{ makeGetValue('desired', 'C_STRUCTS.SDL_AudioSpec.userdata', 'void*', 0, 1) }}},
+ paused: true,
+ timer: null
+ };
+ // The .silence field tells the constant sample value that corresponds to the safe un-skewed silence value for the wave data.
+ if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) {
+ SDL.audio.silence = 128; // Audio ranges in [0, 255], so silence is half-way in between.
+ } else if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) {
+ SDL.audio.silence = 0; // Signed data in range [-32768, 32767], silence is 0.
+ } else {
+ throw 'Invalid SDL audio format ' + SDL.audio.format + '!';
+ }
+ // Round the desired audio frequency up to the next 'common' frequency value.
+ // Web Audio API spec states 'An implementation must support sample-rates in at least the range 22050 to 96000.'
+ if (SDL.audio.freq <= 0) {
+ throw 'Unsupported sound frequency ' + SDL.audio.freq + '!';
+ } else if (SDL.audio.freq <= 22050) {
+ SDL.audio.freq = 22050; // Take it safe and clamp everything lower than 22kHz to that.
+ } else if (SDL.audio.freq <= 32000) {
+ SDL.audio.freq = 32000;
+ } else if (SDL.audio.freq <= 44100) {
+ SDL.audio.freq = 44100;
+ } else if (SDL.audio.freq <= 48000) {
+ SDL.audio.freq = 48000;
+ } else if (SDL.audio.freq <= 96000) {
+ SDL.audio.freq = 96000;
+ } else {
+ throw 'Unsupported sound frequency ' + SDL.audio.freq + '!';
+ }
+ if (SDL.audio.channels == 0) {
+ SDL.audio.channels = 1; // In SDL both 0 and 1 mean mono.
+ } else if (SDL.audio.channels < 0 || SDL.audio.channels > 32) {
+ throw 'Unsupported number of audio channels for SDL audio: ' + SDL.audio.channels + '!';
+ } else if (SDL.audio.channels != 1 && SDL.audio.channels != 2) { // Unsure what SDL audio spec supports. Web Audio spec supports up to 32 channels.
+ console.log('Warning: Using untested number of audio channels ' + SDL.audio.channels);
+ }
+ if (SDL.audio.samples < 1024 || SDL.audio.samples > 524288 /* arbitrary cap */) {
+ throw 'Unsupported audio callback buffer size ' + SDL.audio.samples + '!';
+ } else if ((SDL.audio.samples & (SDL.audio.samples-1)) != 0) {
+ throw 'Audio callback buffer size ' + SDL.audio.samples + ' must be a power-of-two!';
+ }
+
+ var totalSamples = SDL.audio.samples*SDL.audio.channels;
+ SDL.audio.bytesPerSample = (SDL.audio.format == 0x0008 /*AUDIO_U8*/ || SDL.audio.format == 0x8008 /*AUDIO_S8*/) ? 1 : 2;
+ SDL.audio.bufferSize = totalSamples*SDL.audio.bytesPerSample;
+ SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
+
+ // Create a callback function that will be routinely called to ask more audio data from the user application.
+ SDL.audio.caller = function() {
+ if (!SDL.audio) {
+ return;
+ }
+ Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]);
+ SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize);
+ };
+
+ SDL.audio.audioOutput = new Audio();
+ // As a workaround use Mozilla Audio Data API on Firefox until it ships with Web Audio and sound quality issues are fixed.
+ if (typeof(SDL.audio.audioOutput['mozSetup'])==='function') {
+ SDL.audio.audioOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler
+ SDL.audio.mozBuffer = new Float32Array(totalSamples);
+ SDL.audio.nextPlayTime = 0;
+ SDL.audio.pushAudio = function(ptr, size) {
+ var mozBuffer = SDL.audio.mozBuffer;
+ // The input audio data for SDL audio is either 8-bit or 16-bit interleaved across channels, output for Mozilla Audio Data API
+ // needs to be Float32 interleaved, so perform a sample conversion.
+ if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) {
+ for (var i = 0; i < totalSamples; i++) {
+ mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000;
+ }
+ } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) {
+ for (var i = 0; i < totalSamples; i++) {
+ var v = ({{{ makeGetValue('ptr', 'i', 'i8', 0, 0) }}});
+ mozBuffer[i] = ((v >= 0) ? v-128 : v+128) /128;
+ }
+ }
+ // Submit the audio data to audio device.
+ SDL.audio.audioOutput['mozWriteAudio'](mozBuffer);
+
+ // Compute when the next audio callback should be called.
+ var curtime = Date.now() / 1000.0 - SDL.audio.startTime;
+ if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) {
+ console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.');
+ }
+ var playtime = Math.max(curtime, SDL.audio.nextPlayTime);
+ var buffer_duration = SDL.audio.samples / SDL.audio.freq;
+ SDL.audio.nextPlayTime = playtime + buffer_duration;
+ // Schedule the next audio callback call.
+ SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1000.0 * (playtime-curtime));
+ }
+ } else {
+ // Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page,
+ // since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'.
+ if (!SDL.audioContext) {
+ if (typeof(AudioContext) === 'function') {
+ SDL.audioContext = new AudioContext();
+ } else if (typeof(webkitAudioContext) === 'function') {
+ SDL.audioContext = new webkitAudioContext();
+ } else {
+ throw 'Web Audio API is not available!';
+ }
+ }
+ SDL.audio.soundSource = new Array(); // Use an array of sound sources as a ring buffer to queue blocks of synthesized audio to Web Audio API.
+ SDL.audio.nextSoundSource = 0; // Index of the next sound buffer in the ring buffer queue to play.
+ SDL.audio.nextPlayTime = 0; // Time in seconds when the next audio block is due to start.
+
+ // The pushAudio function with a new audio buffer whenever there is new audio data to schedule to be played back on the device.
+ SDL.audio.pushAudio=function(ptr,sizeBytes) {
+ try {
+ --SDL.audio.numAudioTimersPending;
+
+ var sizeSamples = sizeBytes / SDL.audio.bytesPerSample; // How many samples fit in the callback buffer?
+ var sizeSamplesPerChannel = sizeSamples / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer?
+ if (sizeSamplesPerChannel != SDL.audio.samples) {
+ throw 'Received mismatching audio buffer size!';
+ }
+ // Allocate new sound buffer to be played.
+ var source = SDL.audioContext['createBufferSource']();
+ if (SDL.audio.soundSource[SDL.audio.nextSoundSource]) {
+ SDL.audio.soundSource[SDL.audio.nextSoundSource]['disconnect'](); // Explicitly disconnect old source, since we know it shouldn't be running anymore.
+ }
+ SDL.audio.soundSource[SDL.audio.nextSoundSource] = source;
+ var soundBuffer = SDL.audioContext['createBuffer'](SDL.audio.channels,sizeSamplesPerChannel,SDL.audio.freq);
+ SDL.audio.soundSource[SDL.audio.nextSoundSource]['connect'](SDL.audioContext['destination']);
+
+ // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as
+ // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data,
+ // so perform a buffer conversion for the data.
+ var numChannels = SDL.audio.channels;
+ for(var i = 0; i < numChannels; ++i) {
+ var channelData = soundBuffer['getChannelData'](i);
+ if (channelData.length != sizeSamplesPerChannel) {
+ throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + sizeSamplesPerChannel + ' samples!';
+ }
+ if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) {
+ for(var j = 0; j < sizeSamplesPerChannel; ++j) {
+ channelData[j] = ({{{ makeGetValue('ptr', '(j*numChannels + i)*2', 'i16', 0, 0) }}}) / 0x8000;
+ }
+ } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) {
+ for(var j = 0; j < sizeSamplesPerChannel; ++j) {
+ var v = ({{{ makeGetValue('ptr', 'j*numChannels + i', 'i8', 0, 0) }}});
+ channelData[j] = ((v >= 0) ? v-128 : v+128) /128;
+ }
+ }
+ }
+ // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=883675 by setting the buffer only after filling. The order is important here!
+ source['buffer'] = soundBuffer;
+
+ // Schedule the generated sample buffer to be played out at the correct time right after the previously scheduled
+ // sample buffer has finished.
+ var curtime = SDL.audioContext['currentTime'];
+// if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) {
+// console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.');
+// }
+ var playtime = Math.max(curtime, SDL.audio.nextPlayTime);
+ SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](playtime);
+ var buffer_duration = sizeSamplesPerChannel / SDL.audio.freq;
+ SDL.audio.nextPlayTime = playtime + buffer_duration;
+ SDL.audio.nextSoundSource = (SDL.audio.nextSoundSource + 1) % 4;
+ var secsUntilNextCall = playtime-curtime;
+
+ // Queue the next audio frame push to be performed when the previously queued buffer has finished playing.
+ if (SDL.audio.numAudioTimersPending == 0) {
+ var preemptBufferFeedMSecs = buffer_duration/2.0;
+ SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, Math.max(0.0, 1000.0*secsUntilNextCall-preemptBufferFeedMSecs));
+ ++SDL.audio.numAudioTimersPending;
+ }
+
+ // If we are risking starving, immediately queue an extra second buffer.
+ if (secsUntilNextCall <= buffer_duration && SDL.audio.numAudioTimersPending <= 1) {
+ ++SDL.audio.numAudioTimersPending;
+ Browser.safeSetTimeout(SDL.audio.caller, 1.0);
+ }
+ } catch(e) {
+ console.log('Web Audio API error playing back audio: ' + e.toString());
+ }
}
- SDL.audio.mozOutput['mozWriteAudio'](mozBuffer);
}
+
+ if (obtained) {
+ // Report back the initialized audio parameters.
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.freq', 'SDL.audio.freq', 'i32') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.format', 'SDL.audio.format', 'i16') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.channels', 'SDL.audio.channels', 'i8') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.silence', 'SDL.audio.silence', 'i8') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.samples', 'SDL.audio.samples', 'i16') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.callback', 'SDL.audio.callback', '*') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.userdata', 'SDL.audio.userdata', '*') }}};
+ }
+ SDL.allocateChannels(32);
+
} catch(e) {
+ console.log('Initializing SDL audio threw an exception: "' + e.toString() + '"! Continuing without audio.');
SDL.audio = null;
+ SDL.allocateChannels(0);
+ if (obtained) {
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.freq', 0, 'i32') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.format', 0, 'i16') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.channels', 0, 'i8') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.silence', 0, 'i8') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.samples', 0, 'i16') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.callback', 0, '*') }}};
+ {{{ makeSetValue('obtained', 'C_STRUCTS.SDL_AudioSpec.userdata', 0, '*') }}};
+ }
+ }
+ if (!SDL.audio) {
+ return -1;
}
- if (!SDL.audio) return -1;
return 0;
},
SDL_PauseAudio: function(pauseOn) {
- if (SDL.audio.paused !== pauseOn) {
- SDL.audio.timer = pauseOn ? SDL.audio.timer && clearInterval(SDL.audio.timer) : Browser.safeSetInterval(SDL.audio.caller, 1/35);
+ if (!SDL.audio) {
+ return;
+ }
+ if (pauseOn) {
+ if (SDL.audio.timer !== undefined) {
+ clearTimeout(SDL.audio.timer);
+ SDL.audio.numAudioTimersPending = 0;
+ SDL.audio.timer = undefined;
+ }
+ } else if (!SDL.audio.timer) {
+ // Start the audio playback timer callback loop.
+ SDL.audio.numAudioTimersPending = 1;
+ SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1);
+ SDL.audio.startTime = Date.now() / 1000.0; // Only used for Mozilla Audio Data API. Not needed for Web Audio API.
}
SDL.audio.paused = pauseOn;
},
@@ -1438,9 +1621,18 @@ var LibrarySDL = {
SDL_CloseAudio__deps: ['SDL_PauseAudio', 'free'],
SDL_CloseAudio: function() {
if (SDL.audio) {
+ try{
+ for(var i = 0; i < SDL.audio.soundSource.length; ++i) {
+ if (!(typeof(SDL.audio.soundSource[i]==='undefined'))) {
+ SDL.audio.soundSource[i].stop(0);
+ }
+ }
+ } catch(e) {}
+ SDL.audio.soundSource = null;
_SDL_PauseAudio(1);
_free(SDL.audio.buffer);
SDL.audio = null;
+ SDL.allocateChannels(0);
}
},
diff --git a/src/library_sockfs.js b/src/library_sockfs.js
index b11c6495..af29d11b 100644
--- a/src/library_sockfs.js
+++ b/src/library_sockfs.js
@@ -5,12 +5,6 @@ mergeInto(LibraryManager.library, {
mount: function(mount) {
return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
},
- nextname: function() {
- if (!SOCKFS.nextname.current) {
- SOCKFS.nextname.current = 0;
- }
- return 'socket[' + (SOCKFS.nextname.current++) + ']';
- },
createSocket: function(family, type, protocol) {
var streaming = type == {{{ cDefine('SOCK_STREAM') }}};
if (protocol) {
@@ -95,6 +89,12 @@ mergeInto(LibraryManager.library, {
sock.sock_ops.close(sock);
}
},
+ nextname: function() {
+ if (!SOCKFS.nextname.current) {
+ SOCKFS.nextname.current = 0;
+ }
+ return 'socket[' + (SOCKFS.nextname.current++) + ']';
+ },
// backend-specific stream ops
websocket_sock_ops: {
//
diff --git a/src/modules.js b/src/modules.js
index 1a931572..2757c2cb 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -422,7 +422,7 @@ var LibraryManager = {
load: function() {
if (this.library) return;
- var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_memfs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries);
+ var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries);
for (var i = 0; i < libraries.length; i++) {
eval(processMacros(preprocess(read(libraries[i]))));
}
@@ -508,3 +508,7 @@ var PassManager = {
}
};
+var Framework = {
+ currItem: null
+};
+
diff --git a/src/parseTools.js b/src/parseTools.js
index 2ccf0179..7ebc0de2 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -111,12 +111,22 @@ function isNiceIdent(ident, loose) {
}
function isJSVar(ident) {
- return /^\(?[$_]?[\w$_\d ]*\)+$/.test(ident);
-
+ if (ident[0] === '(') {
+ if (ident[ident.length-1] !== ')') return false;
+ ident = ident.substr(1, ident.length-2);
+ }
+ return /^[$_]?[\w$_\d]* *$/.test(ident);
}
function isLocalVar(ident) {
- return ident[0] == '$';
+ return ident[0] === '$';
+}
+
+// Simple variables or numbers, or things already quoted, do not need to be quoted
+function needsQuoting(ident) {
+ if (/^[-+]?[$_]?[\w$_\d]*$/.test(ident)) return false; // number or variable
+ if (ident[0] === '(' && ident[ident.length-1] === ')' && ident.indexOf('(', 1) < 0) return false; // already fully quoted
+ return true;
}
function isStructPointerType(type) {
@@ -933,12 +943,12 @@ function parseLLVMString(str) {
var ret = [];
var i = 0;
while (i < str.length) {
- var chr = str[i];
- if (chr != '\\') {
- ret.push(chr.charCodeAt(0));
+ var chr = str.charCodeAt(i);
+ if (chr !== 92) { // 92 === '//'.charCodeAt(0)
+ ret.push(chr);
i++;
} else {
- ret.push(eval('0x' + str[i+1]+str[i+2]));
+ ret.push(parseInt(str[i+1]+str[i+2], '16'));
i += 3;
}
}
@@ -1197,7 +1207,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
var typeData = Types.types[type];
var ret = [];
for (var i = 0; i < typeData.fields.length; i++) {
- ret.push('f' + i + ': ' + makeGetValue(ptr, pos + typeData.flatIndexes[i], typeData.fields[i], noNeedFirst, unsigned));
+ ret.push('f' + i + ': ' + makeGetValue(ptr, pos + typeData.flatIndexes[i], typeData.fields[i], noNeedFirst, unsigned, 0, 0, noSafe));
}
return '{ ' + ret.join(', ') + ' }';
}
@@ -1205,8 +1215,8 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
// In double mode 1, in x86 we always assume unaligned because we can't trust that; otherwise in le32
// we need this code path if we are not fully aligned.
if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double' && (TARGET_X86 || align < 8)) {
- return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align)) + ',' +
- makeSetTempDouble(1, 'i32', makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align)) + ',' +
+ return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align, noSafe)) + ',' +
+ makeSetTempDouble(1, 'i32', makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align, noSafe)) + ',' +
makeGetTempDouble(0, 'double') + ')';
}
@@ -1219,12 +1229,12 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
if (isIntImplemented(type)) {
if (bytes == 4 && align == 2) {
// Special case that we can optimize
- ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '|' +
- '(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore) + '<<16)';
+ ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore, 2, noSafe) + '|' +
+ '(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore, 2, noSafe) + '<<16)';
} else { // XXX we cannot truly handle > 4... (in x86)
ret = '';
for (var i = 0; i < bytes; i++) {
- ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore) + (i > 0 ? '<<' + (8*i) : '') + ')';
+ ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore, 1, noSafe) + (i > 0 ? '<<' + (8*i) : '') + ')';
if (i < bytes-1) ret += '|';
}
ret = '(' + makeSignOp(ret, type, unsigned ? 'un' : 're', true);
@@ -1303,7 +1313,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
value = range(typeData.fields.length).map(function(i) { return value + '.f' + i });
}
for (var i = 0; i < typeData.fields.length; i++) {
- ret.push(makeSetValue(ptr, getFastValue(pos, '+', typeData.flatIndexes[i]), value[i], typeData.fields[i], noNeedFirst));
+ ret.push(makeSetValue(ptr, getFastValue(pos, '+', typeData.flatIndexes[i]), value[i], typeData.fields[i], noNeedFirst, 0, 0, noSafe));
}
return ret.join('; ');
}
@@ -1330,17 +1340,17 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
if (bytes == 4 && align == 2) {
// Special case that we can optimize
ret += 'tempBigInt=' + value + sep;
- ret += makeSetValue(ptr, pos, 'tempBigInt&0xffff', 'i16', noNeedFirst, ignore, 2) + sep;
- ret += makeSetValue(ptr, getFastValue(pos, '+', 2), 'tempBigInt>>16', 'i16', noNeedFirst, ignore, 2);
+ ret += makeSetValue(ptr, pos, 'tempBigInt&0xffff', 'i16', noNeedFirst, ignore, 2, noSafe) + sep;
+ ret += makeSetValue(ptr, getFastValue(pos, '+', 2), 'tempBigInt>>16', 'i16', noNeedFirst, ignore, 2, noSafe);
} else {
ret += 'tempBigInt=' + value + sep;
for (var i = 0; i < bytes; i++) {
- ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore, 1);
+ ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore, 1, noSafe);
if (i < bytes-1) ret += sep + 'tempBigInt = tempBigInt>>8' + sep;
}
}
} else {
- ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8, null, null, true) + sep;
+ ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8, noSafe, null, true) + sep;
ret += makeCopyValues(getFastValue(ptr, '+', pos), 'tempDoublePtr', Runtime.getNativeTypeSize(type), type, null, align, sep);
}
return ret;
@@ -1349,6 +1359,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
value = indexizeFunctions(value, type);
var offset = calcFastOffset(ptr, pos, noNeedFirst);
+ if (phase === 'pre' && isNumber(offset)) offset += ' '; // avoid pure numeric strings, seem to be perf issues with overly-aggressive interning or slt in pre processing of heap inits
if (SAFE_HEAP && !noSafe) {
var printType = type;
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
@@ -1465,77 +1476,97 @@ function makeHEAPView(which, start, end) {
return 'HEAP' + which + '.subarray((' + start + ')' + mod + ',(' + end + ')' + mod + ')';
}
-var PLUS_MUL = set('+', '*');
-var MUL_DIV = set('*', '/');
-var PLUS_MINUS = set('+', '-');
var TWO_TWENTY = Math.pow(2, 20);
// Given two values and an operation, returns the result of that operation.
// Tries to do as much as possible at compile time.
// Leaves overflows etc. unhandled, *except* for integer multiply, in order to be efficient with Math.imul
function getFastValue(a, op, b, type) {
- a = a.toString();
- b = b.toString();
- a = a == 'true' ? '1' : (a == 'false' ? '0' : a);
- b = b == 'true' ? '1' : (b == 'false' ? '0' : b);
- if (isNumber(a) && isNumber(b)) {
- if (op == 'pow') {
- return Math.pow(a, b).toString();
- } else {
- var value = eval(a + op + '(' + b + ')'); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12"
- if (op == '/' && type in Runtime.INT_TYPES) value = value|0; // avoid emitting floats
- return value.toString();
+ a = a === 'true' ? '1' : (a === 'false' ? '0' : a);
+ b = b === 'true' ? '1' : (b === 'false' ? '0' : b);
+
+ var aNumber = null, bNumber = null;
+ if (typeof a === 'number') {
+ aNumber = a;
+ a = a.toString();
+ } else if (isNumber(a)) aNumber = parseFloat(a);
+ if (typeof b === 'number') {
+ bNumber = b;
+ b = b.toString();
+ } else if (isNumber(b)) bNumber = parseFloat(b);
+
+ if (aNumber !== null && bNumber !== null) {
+ switch (op) {
+ case '+': return (aNumber + bNumber).toString();
+ case '-': return (aNumber - bNumber).toString();
+ case '*': return (aNumber * bNumber).toString();
+ case '/': {
+ if (type[0] === 'i') {
+ return ((aNumber / bNumber)|0).toString();
+ } else {
+ return (aNumber / bNumber).toString();
+ }
+ }
+ case '%': return (aNumber % bNumber).toString();
+ case '|': return (aNumber | bNumber).toString();
+ case '>>>': return (aNumber >>> bNumber).toString();
+ case '&': return (aNumber & bNumber).toString();
+ case 'pow': return Math.pow(aNumber, bNumber).toString();
+ default: throw 'need to implement getFastValue pn ' + op;
}
}
- if (op == 'pow') {
- if (a == '2' && isIntImplemented(type)) {
+ if (op === 'pow') {
+ if (a === '2' && isIntImplemented(type)) {
return '(1 << (' + b + '))';
}
return 'Math.pow(' + a + ', ' + b + ')';
}
- if (op in PLUS_MUL && isNumber(a)) { // if one of them is a number, keep it last
+ if ((op === '+' || op === '*') && aNumber !== null) { // if one of them is a number, keep it last
var c = b;
b = a;
a = c;
- }
- if (op in MUL_DIV) {
- if (op == '*') {
- if (a == 0 || b == 0) {
- return '0';
- } else if (a == 1) {
- return b;
- } else if (b == 1) {
- return a;
- } else if (isNumber(b) && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) {
- var shifts = Math.log(parseFloat(b))/Math.LN2;
- if (shifts % 1 == 0) {
- return '(' + a + '<<' + shifts + ')';
- }
+ var cNumber = bNumber;
+ bNumber = aNumber;
+ aNumber = cNumber;
+ }
+ if (op === '*') {
+ // We can't eliminate where a or b are 0 as that would break things for creating
+ // a negative 0.
+ if ((aNumber === 0 || bNumber === 0) && !(type in Runtime.FLOAT_TYPES)) {
+ return '0';
+ } else if (aNumber === 1) {
+ return b;
+ } else if (bNumber === 1) {
+ return a;
+ } else if (bNumber !== null && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) {
+ var shifts = Math.log(bNumber)/Math.LN2;
+ if (shifts % 1 === 0) {
+ return '(' + a + '<<' + shifts + ')';
}
- if (!(type in Runtime.FLOAT_TYPES)) {
- // if guaranteed small enough to not overflow into a double, do a normal multiply
- var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes
- // Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there
- if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) {
- return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this
- }
- return '(Math.imul(' + a + ',' + b + ')|0)';
+ }
+ if (!(type in Runtime.FLOAT_TYPES)) {
+ // if guaranteed small enough to not overflow into a double, do a normal multiply
+ var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes
+ // Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there
+ if ((aNumber !== null && Math.abs(a) < TWO_TWENTY) || (bNumber !== null && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) {
+ return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this
}
- } else {
- if (a == '0') {
- return '0';
- } else if (b == 1) {
- return a;
- } // Doing shifts for division is problematic, as getting the rounding right on negatives is tricky
- }
- } else if (op in PLUS_MINUS) {
- if (b[0] == '-') {
- op = op == '+' ? '-' : '+';
+ return '(Math.imul(' + a + ',' + b + ')|0)';
+ }
+ } else if (op === '/') {
+ if (a === '0' && !(type in Runtime.FLOAT_TYPES)) { // careful on floats, since 0*NaN is not 0
+ return '0';
+ } else if (b === 1) {
+ return a;
+ } // Doing shifts for division is problematic, as getting the rounding right on negatives is tricky
+ } else if (op === '+' || op === '-') {
+ if (b[0] === '-') {
+ op = op === '+' ? '-' : '+';
b = b.substr(1);
}
- if (a == 0) {
- return op == '+' ? b : '(-' + b + ')';
- } else if (b == 0) {
+ if (aNumber === 0) {
+ return op === '+' ? b : '(-' + b + ')';
+ } else if (bNumber === 0) {
return a;
}
}
@@ -1564,12 +1595,8 @@ function getFastValues(list, op, type) {
}
function calcFastOffset(ptr, pos, noNeedFirst) {
- var offset = noNeedFirst ? '0' : makeGetPos(ptr);
- return getFastValue(offset, '+', pos, 'i32');
-}
-
-function makeGetPos(ptr) {
- return ptr;
+ assert(!noNeedFirst);
+ return getFastValue(ptr, '+', pos, 'i32');
}
var IHEAP_FHEAP = set('IHEAP', 'IHEAPU', 'FHEAP');
@@ -1759,7 +1786,7 @@ function checkBitcast(item) {
} else {
warnOnce('Casting a function pointer type to a potentially incompatible one (use -s VERBOSE=1 to see more)');
}
- warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidlinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts');
+ warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidelinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts');
if (ASM_JS) warnOnce('Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these');
}
if (oldCount != newCount && oldCount && newCount) showWarning();
@@ -1806,7 +1833,7 @@ function getGetElementPtrIndexes(item) {
// struct, and possibly further substructures, all embedded
// can also be to 'blocks': [8 x i32]*, not just structs
type = removePointing(type);
- var indexes = [makeGetPos(ident)];
+ var indexes = [ident];
var offset = item.params[1];
if (offset != 0) {
if (isStructType(type)) {
@@ -1961,13 +1988,12 @@ function makeSignOp(value, type, op, force, ignore) {
if (USE_TYPED_ARRAYS == 2 && type == 'i64') {
return value; // these are always assumed to be two 32-bit unsigneds.
}
-
if (isPointerType(type)) type = 'i32'; // Pointers are treated as 32-bit ints
if (!value) return value;
var bits, full;
if (type in Runtime.INT_TYPES) {
bits = parseInt(type.substr(1));
- full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign())) + ')';
+ full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || correctSpecificSign()) + ')';
// Always sign/unsign constants at compile time, regardless of CHECK/CORRECT
if (isNumber(value)) {
return eval(full).toString();
@@ -1975,23 +2001,25 @@ function makeSignOp(value, type, op, force, ignore) {
}
if ((ignore || !correctSigns()) && !CHECK_SIGNS && !force) return value;
if (type in Runtime.INT_TYPES) {
+ // this is an integer, but not a number (or we would have already handled it)
// shortcuts
if (!CHECK_SIGNS || ignore) {
+ if (value === 'true') {
+ value = '1';
+ } else if (value === 'false') {
+ value = '0';
+ } else if (needsQuoting(value)) value = '(' + value + ')';
if (bits === 32) {
if (op === 're') {
- return '(' + getFastValue(value, '|', '0') + ')';
+ return '(' + value + '|0)';
} else {
-
- return '(' + getFastValue(value, '>>>', '0') + ')';
- // Alternatively, we can consider the lengthier
- // return makeInlineCalculation('VALUE >= 0 ? VALUE : ' + Math.pow(2, bits) + ' + VALUE', value, 'tempBigInt');
- // which does not always turn us into a 32-bit *un*signed value
+ return '(' + value + '>>>0)';
}
} else if (bits < 32) {
if (op === 're') {
- return makeInlineCalculation('(VALUE << ' + (32-bits) + ') >> ' + (32-bits), value, 'tempInt');
+ return '((' + value + '<<' + (32-bits) + ')>>' + (32-bits) + ')';
} else {
- return '(' + getFastValue(value, '&', Math.pow(2, bits)-1) + ')';
+ return '(' + value + '&' + (Math.pow(2, bits)-1) + ')';
}
} else { // bits > 32
if (op === 're') {
@@ -2081,7 +2109,7 @@ function processMathop(item) {
if (item.params[i]) {
paramTypes[i] = item.params[i].type || type;
idents[i] = finalizeLLVMParameter(item.params[i]);
- if (!isNumber(idents[i]) && !isNiceIdent(idents[i])) {
+ if (needsQuoting(idents[i])) {
idents[i] = '(' + idents[i] + ')'; // we may have nested expressions. So enforce the order of operations we want
}
} else {
@@ -2520,3 +2548,8 @@ function makePrintChars(s, sep) {
return ret;
}
+function parseAlign(text) { // parse ", align \d+"
+ if (!text) return QUANTUM_SIZE;
+ return parseInt(text.substr(8));
+}
+
diff --git a/src/postamble.js b/src/postamble.js
index 8f585b86..cd892733 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -35,9 +35,10 @@ var preloadStartTime = null;
var calledMain = false;
var calledRun = false;
-dependenciesFulfilled = function() {
+dependenciesFulfilled = function runCaller() {
// If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
if (!calledRun && shouldRunNow) run();
+ if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled
}
Module['callMain'] = Module.callMain = function callMain(args) {
diff --git a/src/preamble.js b/src/preamble.js
index 02935f8f..183fd0c8 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -299,11 +299,10 @@ function ccallFunc(func, returnType, argTypes, args) {
function toC(value, type) {
if (type == 'string') {
if (value === null || value === undefined || value === 0) return 0; // null string
- if (!stack) stack = Runtime.stackSave();
- var ret = Runtime.stackAlloc(value.length+1);
- writeStringToMemory(value, ret);
- return ret;
- } else if (type == 'array') {
+ value = intArrayFromString(value);
+ type = 'array';
+ }
+ if (type == 'array') {
if (!stack) stack = Runtime.stackSave();
var ret = Runtime.stackAlloc(value.length);
writeArrayToMemory(value, ret);
@@ -717,7 +716,7 @@ var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}};
// Initialize the runtime's memory
#if USE_TYPED_ARRAYS
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
-assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
+assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
'Cannot fallback to non-typed array case: Code is too specialized');
#if USE_TYPED_ARRAYS == 1
@@ -908,6 +907,17 @@ function writeArrayToMemory(array, buffer) {
}
Module['writeArrayToMemory'] = writeArrayToMemory;
+function writeAsciiToMemory(str, buffer, dontAddNull) {
+ for (var i = 0; i < str.length; i++) {
+#if ASSERTIONS
+ assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff);
+#endif
+ {{{ makeSetValue('buffer', 'i', 'str.charCodeAt(i)', 'i8') }}}
+ }
+ if (!dontAddNull) {{{ makeSetValue('buffer', 'str.length', 0, 'i8') }}}
+}
+Module['writeAsciiToMemory'] = writeAsciiToMemory;
+
{{{ unSign }}}
{{{ reSign }}}
@@ -993,8 +1003,9 @@ function removeRunDependency(id) {
runDependencyWatcher = null;
}
if (dependenciesFulfilled) {
- dependenciesFulfilled();
+ var callback = dependenciesFulfilled;
dependenciesFulfilled = null;
+ callback(); // can add another dependenciesFulfilled
}
}
}
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index afb6ecc8..d79dca5a 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -8,8 +8,6 @@
#include "ministring.h"
-// TODO: move all set to unorderedset
-
template <class T, class U> bool contains(const T& container, const U& contained) {
return container.find(contained) != container.end();
}
diff --git a/src/relooper/emscripten/glue.js b/src/relooper/emscripten/glue.js
index 92c50500..587cf529 100644
--- a/src/relooper/emscripten/glue.js
+++ b/src/relooper/emscripten/glue.js
@@ -13,13 +13,16 @@
RelooperGlue['init'] = function() {
this.r = _rl_new_relooper();
},
+ RelooperGlue['cleanup'] = function() {
+ _rl_delete_relooper(this.r);
+ },
RelooperGlue['addBlock'] = function(text, branchVar) {
assert(this.r);
assert(text.length+1 < TBUFFER_SIZE, 'buffer too small, increase RELOOPER_BUFFER_SIZE');
- writeStringToMemory(text, tbuffer);
+ writeAsciiToMemory(text, tbuffer);
if (branchVar) {
assert(branchVar.length+1 < VBUFFER_SIZE, 'buffer too small, increase RELOOPER_BUFFER_SIZE');
- writeStringToMemory(branchVar, vbuffer);
+ writeAsciiToMemory(branchVar, vbuffer);
}
var b = _rl_new_block(tbuffer, branchVar ? vbuffer : 0);
_rl_relooper_add_block(this.r, b);
@@ -29,14 +32,14 @@
assert(this.r);
if (condition) {
assert(condition.length+1 < TBUFFER_SIZE/2, 'buffer too small, increase RELOOPER_BUFFER_SIZE');
- writeStringToMemory(condition, tbuffer);
+ writeAsciiToMemory(condition, tbuffer);
condition = tbuffer;
} else {
condition = 0; // allow undefined, null, etc. as inputs
}
if (code) {
assert(code.length+1 < TBUFFER_SIZE/2, 'buffer too small, increase RELOOPER_BUFFER_SIZE');
- writeStringToMemory(code, tbuffer + TBUFFER_SIZE/2);
+ writeAsciiToMemory(code, tbuffer + TBUFFER_SIZE/2);
code = tbuffer + TBUFFER_SIZE/2;
} else {
code = 0; // allow undefined, null, etc. as inputs
diff --git a/src/runtime.js b/src/runtime.js
index 6b1afd80..00031fed 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -145,29 +145,30 @@ var Runtime = {
return l + h;
},
- //! Returns the size of a type, as C/C++ would have it (in 32-bit, for now), in bytes.
+ //! Returns the size of a type, as C/C++ would have it (in 32-bit), in bytes.
//! @param type The type, by name.
- getNativeTypeSize: function(type, quantumSize) {
- if (Runtime.QUANTUM_SIZE == 1) return 1;
- var size = {
- '%i1': 1,
- '%i8': 1,
- '%i16': 2,
- '%i32': 4,
- '%i64': 8,
- "%float": 4,
- "%double": 8
- }['%'+type]; // add '%' since float and double confuse Closure compiler as keys, and also spidermonkey as a compiler will remove 's from '_i8' etc
- if (!size) {
- if (type.charAt(type.length-1) == '*') {
- size = Runtime.QUANTUM_SIZE; // A pointer
- } else if (type[0] == 'i') {
- var bits = parseInt(type.substr(1));
- assert(bits % 8 == 0);
- size = bits/8;
+ getNativeTypeSize: function(type) {
+#if QUANTUM_SIZE == 1
+ return 1;
+#else
+ switch (type) {
+ case 'i1': case 'i8': return 1;
+ case 'i16': return 2;
+ case 'i32': return 4;
+ case 'i64': return 8;
+ case 'float': return 4;
+ case 'double': return 8;
+ default: {
+ if (type[type.length-1] === '*') {
+ return Runtime.QUANTUM_SIZE; // A pointer
+ } else if (type[0] === 'i') {
+ var bits = parseInt(type.substr(1));
+ assert(bits % 8 === 0);
+ return bits/8;
+ }
}
}
- return size;
+#endif
},
//! Returns the size of a structure field, as C/C++ would have it (in 32-bit,
diff --git a/src/settings.js b/src/settings.js
index 8cdf420c..6e2b9e6c 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -202,6 +202,7 @@ var SOCKET_WEBRTC = 0; // Select socket backend, either webrtc or websockets.
var OPENAL_DEBUG = 0; // Print out debugging information from our OpenAL implementation.
+var GL_ASSERTIONS = 0; // Adds extra checks for error situations in the GL library. Can impact performance.
var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you can set a runtime
// option, in this case GL.debug.
var GL_TESTING = 0; // When enabled, sets preserveDrawingBuffer in the context, to allow tests to work (but adds overhead)
@@ -425,6 +426,8 @@ var JS_CHUNK_SIZE = 10240; // Used as a maximum size before breaking up expressi
var EXPORT_NAME = 'Module'; // Global variable to export the module as for environments without a standardized module
// loading system (e.g. the browser and SM shell).
+var COMPILER_ASSERTIONS = 0; // costly (slow) compile-time assertions
+
// Compiler debugging options
var DEBUG_TAGS_SHOWING = [];
// Some useful items:
diff --git a/src/utility.js b/src/utility.js
index 7d122cef..b793106c 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -334,6 +334,17 @@ function jsonCompare(x, y) {
return JSON.stringify(x) == JSON.stringify(y);
}
+function sortedJsonCompare(x, y) {
+ if (x === null || typeof x !== 'object') return x === y;
+ for (var i in x) {
+ if (!sortedJsonCompare(x[i], y[i])) return false;
+ }
+ for (var i in y) {
+ if (!sortedJsonCompare(x[i], y[i])) return false;
+ }
+ return true;
+}
+
function stringifyWithFunctions(obj) {
if (typeof obj === 'function') return obj.toString();
if (obj === null || typeof obj !== 'object') return JSON.stringify(obj);