aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jsifier.js696
1 files changed, 332 insertions, 364 deletions
diff --git a/src/jsifier.js b/src/jsifier.js
index 462028e0..33553208 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -517,23 +517,42 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// 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 ++;
- });
+ function functionSplitter(item) {
+ item.labels.forEach(function(label) {
+ label.lines.forEach(function(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);
});
-
- this.forwardItems(ret, 'FuncLineTriager');
- }
- });
+ });
+ functionReconstructor(item);
+ }
// function for filtering functions for label debugging
if (LABEL_FUNCTION_FILTERS.length > 0) {
@@ -549,322 +568,296 @@ 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';
+ 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';
+
+ // 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(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)
}
- } 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]]);
}
+ 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];
@@ -880,18 +873,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) {
@@ -926,29 +907,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);
@@ -980,9 +948,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;
@@ -1095,7 +1063,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);
@@ -1116,8 +1084,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) {
@@ -1201,8 +1169,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"
@@ -1215,8 +1183,8 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += ' ' + asmCoercion(value, item.type);
}
return ret + ';';
- });
- makeFuncLineActor('resume', function(item) {
+ }
+ 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.
@@ -1226,8 +1194,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);
@@ -1273,8 +1241,8 @@ 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]);
@@ -1291,8 +1259,8 @@ function JSify(data, functionsOnly, givenFunctions) {
}
default: throw 'unhandled atomic op: ' + item.op;
}
- });
- makeFuncLineActor('landingpad', function(item) {
+ }
+ 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;
@@ -1306,8 +1274,8 @@ 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) {
@@ -1326,8 +1294,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;
@@ -1340,8 +1308,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') {
@@ -1349,24 +1317,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;
@@ -1375,11 +1343,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,
@@ -1388,7 +1356,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'
@@ -1596,21 +1564,21 @@ 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
@@ -1898,7 +1866,7 @@ function JSify(data, functionsOnly, givenFunctions) {
sortGlobals(data.globalVariables).forEach(globalVariableHandler);
data.aliass.forEach(aliasHandler);
- substrate.addItems(data.functions, 'FunctionSplitter');
+ data.functions.forEach(functionSplitter);
}
finalCombiner(substrate.solve());