diff options
-rw-r--r-- | src/jsifier.js | 696 |
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()); |