Skip to content

Support Emscripten EH/SjLj in Wasm64 #14108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 3 additions & 22 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -1521,30 +1521,11 @@ LibraryManager.library = {
gnu_dev_minor: 'minor',

// ==========================================================================
// setjmp.h
// setjmp / longjmp support
// ==========================================================================

longjmp__sig: 'vii',
#if SUPPORT_LONGJMP
longjmp: function(env, value) {
_setThrew(env, value || 1);
throw 'longjmp';
},
#else
longjmp__deps: [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)');
}],
// will never be emitted, as the dep errors at compile time
longjmp: function(env, value) {
abort('longjmp not supported');
},
#endif
// TODO: remove these aliases if/when the LLVM backend can stop emitting them
// (it emits them atm as they are generated by an IR pass, at at that time
// they each have a different signature - it is only at the wasm level that
// they become identical).
emscripten_longjmp__sig: 'vii',
emscripten_longjmp: 'longjmp',
_emscripten_throw_longjmp__sig: 'v',
_emscripten_throw_longjmp: function() { throw 'longjmp'; },

// ==========================================================================
// sys/wait.h
Expand Down
4 changes: 2 additions & 2 deletions system/lib/compiler-rt/emscripten_exception_builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

#include <threads.h>

thread_local int __THREW__ = 0;
thread_local uintptr_t __THREW__ = 0;
thread_local int __threwValue = 0;

void setThrew(int threw, int value) {
void setThrew(uintptr_t threw, int value) {
if (__THREW__ == 0) {
__THREW__ = threw;
__threwValue = value;
Expand Down
21 changes: 15 additions & 6 deletions system/lib/compiler-rt/emscripten_setjmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
#include <stdlib.h>
#include <setjmp.h>

static uint32_t setjmpId = 0;
// 0 - Nothing thrown
// 1 - Exception thrown
// Other values - jmpbuf pointer in the case that longjmp was thrown
static uintptr_t setjmpId = 0;

typedef struct TableEntry {
uint32_t id, label;
uintptr_t id;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is an auto-incrementing counter.. set from the uint32_t setjmpId above. If so why does it need to change type? At least I would expect setjmpId to be of the same type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's tricky... It looks I need to turn even more variables into uintptr_t.

  1. So I described in https://reviews.llvm.org/D101985 how changing the first argument of emscripten_longjmp in turn changed testSetjmp's first argument to uintptr_t.
  2. testSetjmp's first argument id is compared with the local variable curr. So it's uintptr_t now: Link
  3. That curr is set from table[i].id, which is TableEntry.id. So id in TableEntry here is now uintptr_t: Link
  4. Because table[i].id is set from setjmpId, setjmpId needs to be uintptr_t too: Link
  5. Because setjmpId is stored in env (first parameter) in saveSetjmp, saveSetjmp's first argument has to change to uintptr_t: Link

5 was missing in this PR so I added it too. Not sure if this propagation of uintptr_t is good... WDYT?

uint32_t label;
} TableEntry;

extern void setTempRet0(uint32_t value);

TableEntry* saveSetjmp(uint32_t* env, uint32_t label, TableEntry* table, uint32_t size) {
TableEntry* saveSetjmp(uintptr_t* env, uint32_t label, TableEntry* table, uint32_t size) {
// 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.
Expand All @@ -43,10 +47,10 @@ TableEntry* saveSetjmp(uint32_t* env, uint32_t label, TableEntry* table, uint32_
return table;
}

uint32_t testSetjmp(uint32_t id, TableEntry* table, uint32_t size) {
uint32_t i = 0, curr;
uint32_t testSetjmp(uintptr_t id, TableEntry* table, uint32_t size) {
uint32_t i = 0;
while (i < size) {
uint32_t curr = table[i].id;
uintptr_t curr = table[i].id;
if (curr == 0) break;
if (curr == id) {
return table[i].label;
Expand All @@ -55,3 +59,8 @@ uint32_t testSetjmp(uint32_t id, TableEntry* table, uint32_t size) {
}
return 0;
}

void emscripten_longjmp(uintptr_t env, int val) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool... I tried this but I got some odd crashed in the compiler.. but if this works that is great!

setThrew(env, val || 1);
emscripten_throw_longjmp();
}