aboutsummaryrefslogtreecommitdiff
path: root/src/jsifier.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/jsifier.js')
-rw-r--r--src/jsifier.js148
1 files changed, 95 insertions, 53 deletions
diff --git a/src/jsifier.js b/src/jsifier.js
index 21628079..9a3d6ae2 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -7,6 +7,7 @@
var STRUCT_LIST = set('struct', 'list');
var UNDERSCORE_OPENPARENS = set('_', '(');
+var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume');
// JSifier
function JSify(data, functionsOnly, givenFunctions) {
@@ -565,14 +566,14 @@ function JSify(data, functionsOnly, givenFunctions) {
if (true) { // TODO: optimize away when not needed
if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
- func.JS += ' var __label__;\n';
+ func.JS += ' var label;\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) {
+ function getLabelLines(label, indent, relooping) {
if (!label) return '';
var ret = '';
if (LABEL_DEBUG) {
@@ -589,23 +590,35 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// for special labels we care about (for phi), mark that we visited them
- return ret + label.lines.map(function(line) { return line.JS + (Debugging.on ? Debugging.getComment(line.lineNum) : '') })
+ 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++;
+ return JS + (Debugging.on ? Debugging.getComment(line.lineNum) : '');
+ })
.join('\n')
.split('\n') // some lines include line breaks
.map(function(line) { return indent + line })
.join('\n');
}
var ret = '';
- if (block.type == 'emulated') {
+ 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';
+ ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
} // otherwise, should have been set before!
if (func.setjmpTable) {
var setjmpTable = {};
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[0]) + '": ' + 'function(value) { __label__ = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },';
+ ret += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { label = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },';
});
ret += 'dummy: 0';
ret += '};\n';
@@ -614,12 +627,12 @@ function JSify(data, functionsOnly, givenFunctions) {
if (func.setjmpTable) {
ret += 'try { ';
}
- ret += 'switch(__label__) {\n';
+ ret += 'switch(label) {\n';
ret += block.labels.map(function(label) {
return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ getLabelLines(label, indent + ' ');
}).join('\n');
- ret += '\n' + indent + ' default: assert(0, "bad label: " + __label__);\n' + indent + '}';
+ ret += '\n' + indent + ' default: assert(0, "bad label: " + label);\n' + indent + '}';
if (func.setjmpTable) {
ret += ' } catch(e) { if (!e.longjmp) throw(e); setjmpTable[e.label](e.value) }';
}
@@ -627,38 +640,50 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
}
ret += '\n';
- } else if (block.type == 'reloop') {
- ret += indent + block.id + ': while(1) { ' + (SHOW_LABELS ? ' /* ' + block.entries + + ' */' : '') + '\n';
- ret += walkBlock(block.inner, indent + ' ');
- ret += indent + '}\n';
- } else if (block.type == 'multiple') {
- var first = true;
- var multipleIdent = '';
- ret += indent + block.id + ': do { \n';
- multipleIdent = ' ';
- // TODO: Find out cases where the final if/case is not needed - where we know we must be in a specific label at that point
- var SWITCH_IN_MULTIPLE = 0; // This appears to never be worth it, for no amount of labels
- if (SWITCH_IN_MULTIPLE && block.entryLabels.length >= 2) {
- ret += indent + multipleIdent + 'switch(__label__) {\n';
- block.entryLabels.forEach(function(entryLabel) {
- ret += indent + multipleIdent + ' case ' + getLabelId(entryLabel.ident) + ': {\n';
- ret += walkBlock(entryLabel.block, indent + ' ' + multipleIdent);
- ret += indent + multipleIdent + ' } break;\n';
- });
- ret += indent + multipleIdent + '}\n';
- } else {
- block.entryLabels.forEach(function(entryLabel) {
- ret += indent + multipleIdent + (first ? '' : 'else ') + 'if (__label__ == ' + getLabelId(entryLabel.ident) + ') {\n';
- ret += walkBlock(entryLabel.block, indent + ' ' + multipleIdent);
- ret += indent + multipleIdent + '}\n';
- first = false;
- });
- }
- ret += indent + '} while(0);\n';
} else {
- throw "Walked into an invalid block type: " + block.type;
+ // Reloop multiple blocks using the compiled relooper
+
+ //Relooper.setDebug(1);
+ Relooper.init();
+
+ 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');
+ blockMap[label.ident] = Relooper.addBlock(content);
+ }
+ // 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 + walkBlock(block.next, indent);
+ return ret;
}
func.JS += walkBlock(func.block, ' ');
// Finalize function
@@ -687,6 +712,7 @@ function JSify(data, functionsOnly, givenFunctions) {
func.JS += 'if (globalScope) { assert(!globalScope["' + func.ident + '"]); globalScope["' + func.ident + '"] = ' + func.ident + ' }';
}
+ func.JS = func.JS.replace(/\n *;/g, '\n'); // remove unneeded lines
return func;
}
});
@@ -821,7 +847,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var parts = label.split('|');
var trueLabel = parts[1] || '';
var oldLabel = parts[2] || '';
- var labelSetting = oldLabel ? '__label__ = ' + getLabelId(oldLabel) + ';' +
+ var labelSetting = oldLabel ? 'label = ' + getLabelId(oldLabel) + ';' +
(SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(oldLabel)) + ' */' : '') : ''; // TODO: optimize away the setting
if (label[1] == 'R') {
if (label[2] == 'N') { // BRNOL: break, no label setting
@@ -842,7 +868,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
} else {
if (!labelIsVariable) label = getLabelId(label);
- return pre + '__label__ = ' + label + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(label)) + ' */' : '') + ' break;';
+ return pre + 'label = ' + label + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(label)) + ' */' : '') + ' break;';
}
}
@@ -918,11 +944,11 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('branch', function(item) {
var phiSets = calcPhiSets(item);
if (!item.value) {
- return getPhiSetsForLabel(phiSets, item.label) + makeBranch(item.label, item.currLabelId);
+ return (item.labelJS = getPhiSetsForLabel(phiSets, item.label)) + makeBranch(item.label, item.currLabelId);
} else {
- var condition = finalizeLLVMParameter(item.value);
- var labelTrue = getPhiSetsForLabel(phiSets, item.labelTrue) + makeBranch(item.labelTrue, item.currLabelId);
- var labelFalse = getPhiSetsForLabel(phiSets, item.labelFalse) + makeBranch(item.labelFalse, item.currLabelId);
+ var condition = item.valueJS = finalizeLLVMParameter(item.value);
+ var labelTrue = (item.labelTrueJS = getPhiSetsForLabel(phiSets, item.labelTrue)) + makeBranch(item.labelTrue, item.currLabelId);
+ var labelFalse = (item.labelFalseJS = getPhiSetsForLabel(phiSets, item.labelFalse)) + makeBranch(item.labelFalse, item.currLabelId);
if (labelTrue == ';' && labelFalse == ';') return ';';
var head = 'if (' + condition + ') { ';
var head2 = 'if (!(' + condition + ')) { ';
@@ -940,11 +966,11 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('switch', function(item) {
// TODO: Find a case where switch is important, and benchmark that. var SWITCH_IN_SWITCH = 1;
var phiSets = calcPhiSets(item);
- // Consolidate checks that go to the same label. This is important because it makes the
- // js optimizer hoistMultiples much easier to implement (we hoist into one place, not
- // many).
+ // Consolidate checks that go to the same label. This is important because it makes the relooper simpler and faster.
var targetLabels = {}; // for each target label, the list of values going to it
+ var switchLabelMap = {};
item.switchLabels.forEach(function(switchLabel) {
+ switchLabelMap[switchLabel.label] = switchLabel;
if (!targetLabels[switchLabel.label]) {
targetLabels[switchLabel.label] = [];
}
@@ -953,20 +979,33 @@ function JSify(data, functionsOnly, givenFunctions) {
var ret = '';
var first = true;
var signedIdent = makeSignOp(item.ident, item.type, 're'); // we need to standardize for purpose of comparison
+ if (RELOOP) {
+ item.groupedLabels = [];
+ }
for (var targetLabel in targetLabels) {
if (!first) {
ret += 'else ';
} else {
first = false;
}
- ret += 'if (' + targetLabels[targetLabel].map(function(value) {
+ var value = targetLabels[targetLabel].map(function(value) {
return makeComparison(signedIdent, makeSignOp(value, item.type, 're'), item.type)
- }).join(' || ') + ') {\n';
- ret += ' ' + getPhiSetsForLabel(phiSets, targetLabel) + makeBranch(targetLabel, item.currLabelId || null) + '\n';
+ }).join(' || ');
+ ret += 'if (' + value + ') {\n';
+ var phiSet = getPhiSetsForLabel(phiSets, targetLabel);
+ ret += ' ' + phiSet + makeBranch(targetLabel, item.currLabelId || null) + '\n';
ret += '}\n';
+ if (RELOOP) {
+ item.groupedLabels.push({
+ label: targetLabel,
+ value: value,
+ labelJS: phiSet
+ });
+ }
}
if (item.switchLabels.length > 0) ret += 'else {\n';
- ret += getPhiSetsForLabel(phiSets, item.defaultLabel) + makeBranch(item.defaultLabel, item.currLabelId) + '\n';
+ var phiSet = item.defaultLabelJS = getPhiSetsForLabel(phiSets, item.defaultLabel);
+ ret += phiSet + makeBranch(item.defaultLabel, item.currLabelId) + '\n';
if (item.switchLabels.length > 0) ret += '}\n';
if (item.value) {
ret += ' ' + toNiceIdent(item.value);
@@ -1015,8 +1054,11 @@ function JSify(data, functionsOnly, givenFunctions) {
}
item.assignTo = null;
}
- ret += 'if (!__THREW__) { ' + getPhiSetsForLabel(phiSets, item.toLabel) + makeBranch(item.toLabel, item.currLabelId)
- + ' } else { ' + getPhiSetsForLabel(phiSets, item.unwindLabel) + makeBranch(item.unwindLabel, item.currLabelId) + ' }';
+ item.reloopingJS = ret; // everything but the actual branching (which the relooper will do for us)
+ item.toLabelJS = getPhiSetsForLabel(phiSets, item.toLabel);
+ item.unwindLabelJS = getPhiSetsForLabel(phiSets, item.unwindLabel);
+ ret += 'if (!__THREW__) { ' + item.toLabelJS + makeBranch(item.toLabel, item.currLabelId)
+ + ' } else { ' + item.unwindLabelJS + makeBranch(item.unwindLabel, item.currLabelId) + ' }';
return ret;
});
makeFuncLineActor('atomic', function(item) {