Skip to content

Commit 929d3e5

Browse files
committed
Micro-optimize performance and code size generated for longjmp in Emscripten invokes to avoid string comparisons
Gate code that is only used for Emscripten EH and not Wasm EH behind `#if SUPPORT_LONGJMP == 'emscripten'` / `#ifndef __USING_WASM_SJLJ__`
1 parent ea06e07 commit 929d3e5

File tree

5 files changed

+22
-12
lines changed

5 files changed

+22
-12
lines changed

src/library.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1323,8 +1323,11 @@ LibraryManager.library = {
13231323
// setjmp.h
13241324
// ==========================================================================
13251325

1326+
#if SUPPORT_LONGJMP == 'emscripten'
13261327
_emscripten_throw_longjmp__sig: 'v',
1327-
_emscripten_throw_longjmp: function() { throw 'longjmp'; },
1328+
_emscripten_throw_longjmp: function() { throw Infinity; },
1329+
#endif
1330+
13281331
#if !SUPPORT_LONGJMP
13291332
#if !INCLUDE_FULL_LIBRARY
13301333
// These are in order to print helpful error messages when either longjmp of
@@ -1340,6 +1343,7 @@ LibraryManager.library = {
13401343
// built with SUPPORT_LONGJMP=1, the object file contains references of not
13411344
// longjmp but _emscripten_throw_longjmp, which is called from
13421345
// emscripten_longjmp.
1346+
_emscripten_throw_longjmp: function() { error('longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)'); },
13431347
get _emscripten_throw_longjmp__deps() {
13441348
return this.longjmp__deps;
13451349
},

src/library_dylink.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,10 @@ var LibraryDylink = {
242242
return dynCall(sig, arguments[0], Array.prototype.slice.call(arguments, 1));
243243
} catch(e) {
244244
stackRestore(sp);
245-
if (e !== e+0 && e !== 'longjmp') throw e;
245+
// Exceptions thrown from C++ exception will be integer numbers.
246+
// longjmp will throw the number Infinity. Re-throw other types of
247+
// exceptions using a compact and fast check.
248+
if (e !== e+0) throw e;
246249
_setThrew(1, 0);
247250
}
248251
}

src/preamble.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ function makeAbortWrapper(original) {
683683
ABORT // rethrow exception if abort() was called in the original function call above
684684
|| abortWrapperDepth > 1 // rethrow exceptions not caught at the top level if exception catching is enabled; rethrow from exceptions from within callMain
685685
#if SUPPORT_LONGJMP == 'emscripten'
686-
|| e === 'longjmp' // rethrow longjmp if enabled
686+
|| e === Infinity // rethrow longjmp if enabled (In Emscripten EH format longjmp will throw Infinity)
687687
#endif
688688
) {
689689
throw e;

system/lib/compiler-rt/emscripten_setjmp.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ typedef struct TableEntry {
2222

2323
extern void setTempRet0(uint32_t value);
2424
extern void setThrew(uintptr_t threw, int value);
25-
extern void _emscripten_throw_longjmp(); // defined in src/library.js
2625

2726
TableEntry* saveSetjmp(uintptr_t* env, uint32_t label, TableEntry* table, uint32_t size) {
2827
// Not particularly fast: slow table lookup of setjmpId to label. But setjmp
@@ -63,10 +62,15 @@ uint32_t testSetjmp(uintptr_t id, TableEntry* table, uint32_t size) {
6362
return 0;
6463
}
6564

65+
#ifndef __USING_WASM_SJLJ__
66+
67+
extern void _emscripten_throw_longjmp(); // defined in src/library.js
68+
6669
void emscripten_longjmp(uintptr_t env, int val) {
6770
setThrew(env, val);
6871
_emscripten_throw_longjmp();
6972
}
73+
#endif
7074

7175
#ifdef __USING_WASM_SJLJ__
7276

tools/js_manipulation.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,22 +120,21 @@ def make_invoke(sig):
120120
# wasm won't implicitly convert undefined to 0 in this case.
121121
exceptional_ret = '\n return BigInt(0);' if legal_sig[0] == 'j' else ''
122122
body = '%s%s;' % (ret, make_dynCall(sig, args))
123-
# C++ exceptions are numbers, and longjmp is a string 'longjmp'
124-
if settings.SUPPORT_LONGJMP:
125-
rethrow = "if (e !== e+0 && e !== 'longjmp') throw e;"
126-
else:
127-
rethrow = "if (e !== e+0) throw e;"
128-
123+
# Exceptions thrown from C++ exception will be integer numbers.
124+
# longjmp will throw the number Infinity.
125+
# Create a try-catch guard that rethrows the exception if anything else
126+
# than a Number was thrown. To do that quickly and in a code size conserving
127+
# manner, use the compact test "e !== e+0" to check if e was not a Number.
129128
ret = '''\
130129
function invoke_%s(%s) {
131130
var sp = stackSave();
132131
try {
133132
%s
134133
} catch(e) {
135134
stackRestore(sp);
136-
%s
135+
if (e !== e+0) throw e;
137136
_setThrew(1, 0);%s
138137
}
139-
}''' % (sig, ','.join(args), body, rethrow, exceptional_ret)
138+
}''' % (sig, ','.join(args), body, exceptional_ret)
140139

141140
return ret

0 commit comments

Comments
 (0)