aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2012-02-18 12:01:57 -0800
committerAlon Zakai <alonzakai@gmail.com>2012-02-18 12:01:57 -0800
commit52897895fb947665984a784555405ee0a806af18 (patch)
treef3bf5efac22ce12ddff4db971b4239c7c14ed5e6
parentc4e578be4cc6f5a6d801b8106422eb30de725b18 (diff)
basic support for setjmp/longjmp
-rw-r--r--src/analyzer.js59
-rw-r--r--src/jsifier.js18
-rw-r--r--src/library.js21
-rwxr-xr-xtests/runner.py35
4 files changed, 118 insertions, 15 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 54e1ab43..14a4f7de 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -11,11 +11,8 @@ var VAR_EMULATED = 'emulated';
var ENTRY_IDENT = toNiceIdent('%0');
var ENTRY_IDENTS = set(toNiceIdent('%0'), toNiceIdent('%1'));
-function cleanFunc(func) {
- func.lines = func.lines.filter(function(line) { return line.intertype !== null });
- func.labels.forEach(function(label) {
- label.lines = label.lines.filter(function(line) { return line.intertype !== null });
- });
+function recomputeLines(func) {
+ func.lines = func.labels.map(function(label) { return label.lines }).reduce(concatenator, []);
}
// Handy sets
@@ -71,6 +68,7 @@ function analyzer(data, sidePass) {
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') {
@@ -1111,10 +1109,9 @@ function analyzer(data, sidePass) {
});
func.labelIds[toNiceIdent('%0')] = -1; // entry is always -1
- func.hasIndirectBr = false;
func.lines.forEach(function(line) {
if (line.intertype == 'indirectbr') {
- func.hasIndirectBr = true;
+ func.forceEmulated = true;
}
});
@@ -1129,6 +1126,52 @@ function analyzer(data, sidePass) {
return null;
}
+ // 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.ident == setjmp) {
+ // Add a new label
+ var oldIdent = label.ident;
+ var newIdent = oldIdent + '$$' + i;
+ if (!func.setjmpTable) func.setjmpTable = [];
+ func.setjmpTable.push([oldIdent, newIdent, line.assignTo]);
+ func.labels.splice(i+1, 0, {
+ intertype: 'label',
+ ident: newIdent,
+ lineNum: label.lineNum + 0.5,
+ lines: label.lines.slice(j+1)
+ });
+ label.lines = label.lines.slice(0, j+1);
+ label.lines.push({
+ intertype: 'branch',
+ label: toNiceIdent(newIdent),
+ 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 == oldIdent) {
+ phi.params[i].label = newIdent;
+ }
+ }
+ }
+ });
+ });
+ }
+ }
+ }
+ if (func.setjmpTable) {
+ func.forceEmulated = true;
+ recomputeLines(func);
+ }
+
if (!MICRO_OPTS) {
// 'Emulate' phis, by doing an if where the phi appears in the .ll. For this
// we need __lastLabel__.
@@ -1744,7 +1787,7 @@ function analyzer(data, sidePass) {
// TODO: each of these can be run in parallel
item.functions.forEach(function(func) {
dprint('relooping', "// relooping function: " + func.ident);
- func.block = makeBlock(func.labels, [toNiceIdent(func.labels[0].ident)], func.labelsDict, func.hasIndirectBr);
+ func.block = makeBlock(func.labels, [toNiceIdent(func.labels[0].ident)], func.labelsDict, func.forceEmulated);
});
return finish();
diff --git a/src/jsifier.js b/src/jsifier.js
index b830fc7c..7caeea61 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -576,12 +576,28 @@ function JSify(data, functionsOnly, givenFunctions) {
if (block.entries.length == 1) {
ret += indent + '__label__ = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + block.entries[0] + ' */' : '') + '\n';
} // otherwise, should have been set before!
- ret += indent + 'while(1) switch(__label__) {\n';
+ 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 += 'dummy: 0';
+ ret += '};\n';
+ }
+ ret += indent + 'while(1) ';
+ if (func.setjmpTable) {
+ ret += 'try { ';
+ }
+ ret += 'switch(__label__) {\n';
ret += block.labels.map(function(label) {
return indent + ' case ' + getLabelId(label.ident) + ': // ' + label.ident + '\n'
+ getLabelLines(label, indent + ' ');
}).join('\n');
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) }';
+ }
} else {
ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
}
diff --git a/src/library.js b/src/library.js
index e0a38817..36aaef77 100644
--- a/src/library.js
+++ b/src/library.js
@@ -5278,17 +5278,26 @@ LibraryManager.library = {
// ==========================================================================
// setjmp.h
+ //
+ // Basic support for setjmp/longjmp: enough to run the wikipedia example and
+ // hopefully handle most normal behavior. We do not support cases where
+ // longjmp behavior is undefined (for example, if the setjmp function returns
+ // before longjmp is called).
+ //
+ // Note that we need to emulate functions that use setjmp, and also to create
+ // a new label we can return to. Emulation make such functions slower, this
+ // can be alleviated by making a new function containing just the setjmp
+ // related functionality so the slowdown is more limited.
// ==========================================================================
- setjmp: function(env) {
- // XXX print('WARNING: setjmp() not really implemented, will fail if longjmp() is actually called');
- return 0;
+ setjmp__inline: function(env) {
+ // Save the label
+ return '(' + makeSetValue(env, '0', '__label__', 'i32') + ', 0)';
},
_setjmp: 'setjmp',
- longjmp: function(env, val) {
- // not really working...
- assert(0);
+ longjmp: function(env, value) {
+ throw { longjmp: true, label: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
},
_longjmp: 'longjmp',
diff --git a/tests/runner.py b/tests/runner.py
index 3d27d2c2..44fba344 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -1344,6 +1344,41 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
'''
self.do_run(src, 'Assertion failed: 1 == false')
+ def test_longjmp(self):
+ src = r'''
+ #include <stdio.h>
+ #include <setjmp.h>
+
+ static jmp_buf buf;
+
+ void second(void) {
+ printf("second\n"); // prints
+ longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1
+ }
+
+ void first(void) {
+ second();
+ printf("first\n"); // does not print
+ }
+
+ int main() {
+ int x = 0;
+ if ( ! setjmp(buf) ) {
+ x++;
+ first(); // when executed, setjmp returns 0
+ } else { // when longjmp jumps back, setjmp returns 1
+ printf("main: %d\n", x); // prints
+ }
+
+ return 0;
+ }
+ '''
+ # gcc -O0 and -O2 differ in what they do with the saved state of local vars - and we match that
+ if self.emcc_args is None or ('-O1' not in self.emcc_args and '-O2' not in self.emcc_args):
+ self.do_run(src, 'second\nmain: 1\n')
+ else:
+ self.do_run(src, 'second\nmain: 0\n')
+
def test_exceptions(self):
if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1")