aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemscripten.py8
-rw-r--r--src/analyzer.js16
-rw-r--r--src/jsifier.js44
-rw-r--r--src/library.js60
-rw-r--r--src/preamble.js2
-rw-r--r--src/settings.js1
-rw-r--r--tests/cases/longjmp_tiny.ll (renamed from tests/cases/longjmp_tiny_noasm.ll)0
-rw-r--r--tests/cases/longjmp_tiny.txt (renamed from tests/cases/longjmp_tiny_noasm.txt)0
8 files changed, 110 insertions, 21 deletions
diff --git a/emscripten.py b/emscripten.py
index 6e5f1e7c..0fa6ac30 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -426,7 +426,7 @@ function invoke_%s(%s) {
try {
%sModule.dynCall_%s(%s);
} catch(e) {
- asm.setThrew(1);
+ asm.setThrew(1, 0);
}
}
''' % (sig, args, 'return ' if sig[0] != 'v' else '', sig, args)
@@ -489,6 +489,8 @@ var asm = (function(global, env, buffer) {
var HEAPF64 = new global.Float64Array(buffer);
''' % (asm_setup,) + '\n' + asm_global_vars + '''
var __THREW__ = 0;
+ var threwValue = 0;
+ var setjmpId = 0;
var undef = 0;
var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
''' + ''.join(['''
@@ -509,9 +511,11 @@ var asm = (function(global, env, buffer) {
top = top|0;
STACKTOP = top;
}
- function setThrew(threw) {
+ function setThrew(threw, value) {
threw = threw|0;
+ value = value|0;
__THREW__ = threw;
+ threwValue = value;
}
''' + ''.join(['''
function setTempRet%d(value) {
diff --git a/src/analyzer.js b/src/analyzer.js
index df5a435e..3278139b 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -1389,21 +1389,21 @@ function analyzer(data, sidePass) {
var line = label.lines[j];
if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
// Add a new label
- var oldIdent = label.ident;
- var newIdent = func.labelIdCounter++;
+ var oldLabel = label.ident;
+ var newLabel = func.labelIdCounter++;
if (!func.setjmpTable) func.setjmpTable = [];
- func.setjmpTable.push([oldIdent, newIdent, line.assignTo]);
+ func.setjmpTable.push({ oldLabel: oldLabel, newLabel: newLabel, assignTo: line.assignTo });
func.labels.splice(i+1, 0, {
intertype: 'label',
- ident: newIdent,
+ ident: newLabel,
lineNum: label.lineNum + 0.5,
lines: label.lines.slice(j+1)
});
- func.labelsDict[newIdent] = func.labels[i+1];
+ func.labelsDict[newLabel] = func.labels[i+1];
label.lines = label.lines.slice(0, j+1);
label.lines.push({
intertype: 'branch',
- label: toNiceIdent(newIdent),
+ label: toNiceIdent(newLabel),
lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this
});
// Correct phis
@@ -1412,8 +1412,8 @@ function analyzer(data, sidePass) {
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 (sourceLabelId == oldLabel) {
+ phi.params[i].label = newLabel;
}
}
}
diff --git a/src/jsifier.js b/src/jsifier.js
index a4dd797b..7086922c 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -701,18 +701,22 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
} // otherwise, should have been set before!
if (func.setjmpTable) {
- assert(!ASM_JS, 'asm.js mode does not support setjmp yet');
- 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[0]) + '": ' + 'function(value) { label = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },';
- });
- ret += 'dummy: 0';
- ret += '};\n';
+ 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 += indent + 'while(1) ';
- if (func.setjmpTable) {
+ if (func.setjmpTable && !ASM_JS) {
ret += 'try { ';
}
ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n';
@@ -720,9 +724,19 @@ function JSify(data, functionsOnly, givenFunctions) {
return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ getLabelLines(label, indent + ' ');
}).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 += ' case -1111: ';
+ 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 + ' default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
ret += indent + '}\n';
- if (func.setjmpTable) {
+ if (func.setjmpTable && !ASM_JS) {
ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
}
} else {
@@ -1301,6 +1315,8 @@ function JSify(data, functionsOnly, givenFunctions) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
+ if (ASM_JS && funcData.setjmpTable) forceByPointer = true; // in asm.js mode, we must do an invoke for each call
+
ident = Variables.resolveAliasToIdent(ident);
var shortident = ident.slice(1);
var simpleIdent = shortident;
@@ -1455,6 +1471,12 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += '; return 0'; // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return
}
}
+
+ if (ASM_JS && funcData.setjmpTable) {
+ // check if a longjmp was done
+ ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) > 0)) { setjmpLabel = _testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable); if ((setjmpLabel|0) > 0) { label = -1111; break } } __THREW__ = threwValue = 0;\n';
+ }
+
return ret;
}
makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) });
diff --git a/src/library.js b/src/library.js
index ee01abcd..95aa2ede 100644
--- a/src/library.js
+++ b/src/library.js
@@ -6217,13 +6217,73 @@ LibraryManager.library = {
// related functionality so the slowdown is more limited.
// ==========================================================================
+ saveSetjmp__asm: true,
+ saveSetjmp__sig: 'iii',
+ saveSetjmp: function(env, label, table) {
+ // Not particularly fast: slow table lookup of setjmpId to label. But setjmp
+ // prevents relooping anyhow, so slowness is to be expected. And typical case
+ // is 1 setjmp per invocation, or less.
+ env = env|0;
+ label = label|0;
+ table = table|0;
+ var i = 0;
+#if ASSERTIONS
+ if ((label|0) == 0) abort(121);
+#endif
+ setjmpId = (setjmpId+1)|0;
+ {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}};
+ while ((i|0) < {{{ MAX_SETJMPS }}}) {
+ if ({{{ makeGetValueAsm('table', 'i*4', 'i32') }}} == 0) {
+ {{{ makeSetValueAsm('table', 'i*4', 'setjmpId', 'i32') }}};
+ {{{ makeSetValueAsm('table', 'i*4+4', 'label', 'i32') }}};
+ // prepare next slot
+ {{{ makeSetValueAsm('table', 'i*4+8', '0', 'i32') }}};
+ return 0;
+ }
+ i = (i+2)|0;
+ }
+ abort(987); // if you hit this, adjust MAX_SETJMPS
+ return 0;
+ },
+
+ testSetjmp__asm: true,
+ testSetjmp__sig: 'iii',
+ testSetjmp: function(id, table) {
+ id = id|0;
+ table = table|0;
+ var i = 0, curr = 0;
+ while ((i|0) < {{{ MAX_SETJMPS }}}) {
+ curr = {{{ makeGetValueAsm('table', 'i', 'i32') }}};
+ if ((curr|0) == 0) break;
+ if ((curr|0) == (id|0)) {
+ return {{{ makeGetValueAsm('table', 'i+1', 'i32') }}};
+ }
+ i = (i+2)|0;
+ }
+ return 0;
+ },
+
+#if ASM_JS
+ setjmp__deps: ['saveSetjmp', 'testSetjmp'],
+#endif
setjmp__inline: function(env) {
// Save the label
+#if ASM_JS
+ return '_saveSetjmp(' + env + ', label, setjmpTable)';
+#else
return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32', undefined, undefined, undefined, undefined, ',') + ', 0)';
+#endif
},
+#if ASM_JS
+ longjmp__deps: ['saveSetjmp', 'testSetjmp'],
+#endif
longjmp: function(env, value) {
+#if ASM_JS
+ asm.setThrew(env, value || 1);
+#else
throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
+#endif
},
// ==========================================================================
diff --git a/src/preamble.js b/src/preamble.js
index 592363f9..17b74cd9 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -217,8 +217,10 @@ var START_TIME = Date.now();
//========================================
var __THREW__ = 0; // Used in checking for thrown exceptions.
+#if ASM_JS == 0
var setjmpId = 1; // Used in setjmp/longjmp
var setjmpLabels = {};
+#endif
var ABORT = false;
diff --git a/src/settings.js b/src/settings.js
index 8f31c2d8..db24cca4 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -55,6 +55,7 @@ var ALLOW_MEMORY_GROWTH = 0; // If false, we abort with an error if we try to al
// that case we must be careful about optimizations, in particular the
// eliminator). Note that memory growth is only supported with typed
// arrays.
+var MAX_SETJMPS = 10; // size of setjmp table allocated in each function invocation (that has setjmp)
// Code embetterments
var MICRO_OPTS = 1; // Various micro-optimizations, like nativizing variables
diff --git a/tests/cases/longjmp_tiny_noasm.ll b/tests/cases/longjmp_tiny.ll
index 0045847c..0045847c 100644
--- a/tests/cases/longjmp_tiny_noasm.ll
+++ b/tests/cases/longjmp_tiny.ll
diff --git a/tests/cases/longjmp_tiny_noasm.txt b/tests/cases/longjmp_tiny.txt
index 8a0aa386..8a0aa386 100644
--- a/tests/cases/longjmp_tiny_noasm.txt
+++ b/tests/cases/longjmp_tiny.txt