diff options
-rwxr-xr-x | emscripten.py | 8 | ||||
-rw-r--r-- | src/analyzer.js | 16 | ||||
-rw-r--r-- | src/jsifier.js | 44 | ||||
-rw-r--r-- | src/library.js | 60 | ||||
-rw-r--r-- | src/preamble.js | 2 | ||||
-rw-r--r-- | src/settings.js | 1 | ||||
-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 |