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

Conversation

aheejin
Copy link
Member

@aheejin aheejin commented May 6, 2021

This reflects changes in the function signature in
https://reviews.llvm.org/D101985 and also uses from64 function defined
in #12869. This has not been tested against wasm64 yet and I'm uploading
this mainly for discussions at this point.

Also in the team chat of the tools team we discussed other alternatives
to from64 function, which can be done separately and not reflected
here.

This reflects changes in the function signature in
https://reviews.llvm.org/D101985 and also uses `from64` function defined
in emscripten-core#12869. This has not been tested against wasm64 yet and I'm uploading
this mainly for discussions at this point.

Also in the team chat of the tools team we discussed other alternatives
to `from64` function, which can be done separately and not reflected
here.
@aheejin aheejin requested review from sbc100 and aardappel May 6, 2021 10:44
src/library.js Outdated
@@ -1527,6 +1527,7 @@ LibraryManager.library = {
longjmp__sig: 'vii',
#if SUPPORT_LONGJMP
longjmp: function(env, value) {
{{{ from64('env') }}};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looking at this I think we can maybe avoid this change by moving longjmp itself into have code and then renaming this functions to just be _emscripten_throw_longjmp:

_emscripten_throw_longjump: function() { throw 'longjmp'; }

Copy link
Member Author

Choose a reason for hiding this comment

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

That sounds really good if possible. But not sure how to resolve this case.

There are two cases we generate a call to emscripten_longjmp in WebAssemblyLowerEmscriptenEHSjLj pass in LLVM;

  1. Replace C longjmp call with emscripten_longjmp: Detail link
  2. Transform every call that might longjmp into some complex code sequence, when emscripten_longjmp is called somewhere in the middle of it: Detail link

The second case is fine, because we know the two parameters to emscripten_longjmp while generating the call. The first case is tricky, because existing longjmp calls can be indirect calls, in which case we don't know those two arguments to longjmp are. So in that case we just cast the value to the type of emscripten_longjmp: (i32, i32). Because we don't know the two parameters, we can't generate setThrew and emscripten_throw_longjmp sequence there. So with this I think it will be hard to remove emscripten_longjmp function. WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I wasn't suggesting changing anything about codegen. Only that we move this function from js code the c code. It shouldn't be a change the makes any difference to the calling code.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh I misunderstood what you said, sorry. Yeah, will do.

@@ -12,7 +12,8 @@
static uint32_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?

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

You can also remove the 'emscripten_longjmp': ['setThrew'], line from deps_info.py now I think!

And maybe also 'longjmp': ['setThrew'] ... not sure..

@@ -56,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!

@aheejin aheejin marked this pull request as ready for review May 13, 2021 07:11
Copy link
Member Author

@aheejin aheejin left a comment

Choose a reason for hiding this comment

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

Thanks for all the comments!

The previous version didn't pass testing; now this passes all existing tests. I don't have a full grasp on all interactions between JS/C dependences, so this PR is somehow the result of trail and error until I find the combination that works..

Couldn't test for wasm64 yet because its support has not landed yet, but I think there will not likely to be any blockers for wasm64 after this.

You can also remove the 'emscripten_longjmp': ['setThrew'], line from deps_info.py now I think!

I couldn't. So the LLVM-generated code has emscripten_longjmp as a undefined symbol, but without this line, add_reverse_deps does not add setThrew to settings.EXPORTED_FUNCTIONS. I'm slightly confused because the comment in deps_info.py says that file is for JS->C dependences, but I still need this line even though now both emscripten_longjmp and setThrew are in C. Do you think this is how it's supposed to work?

And maybe also 'longjmp': ['setThrew'] ... not sure..

I was able to remove it because I removed longjmp anyway from src/library.js. I wonder why we were keeping two different versions (longjmp and emscripten_longjmp) in JS before..? Is it safe to remove now? It doesn't seem to affect tests though.


Edit: I thought all tests were passing but test_longjmp_standalone is failing. Investigating now. It fails with this message:

/usr/bin/ld: /tmp/test_longjmp-91065b.o: in function `w2c_f14':
test_longjmp.wasm.c:(.text+0x837e): undefined reference to `Z_envZ__emscripten_throw_longjmpZ_vv'

Do you have any idea why?

@@ -138,20 +138,16 @@ var funs = {
return -1;
},
#if SUPPORT_LONGJMP
#if ASSERTIONS
siglongjmp__deps: ['longjmp'],
Copy link
Member Author

Choose a reason for hiding this comment

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

What is this __deps and how is this different from entries in deps_info.py? I deleted this because tests run correctly without this, but not sure what this is for.

},
#else
siglongjmp__sig: 'vii',
siglongjmp: 'longjmp',
Copy link
Member Author

Choose a reason for hiding this comment

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

I first tried to change this to

siglongjmp: '_emscripten_longjmp'

But then wasm0.test_longjmp succeeds but wasm1.test_longjmp fails saying "no alias for siglongjmp[here](https://github.com/emscripten-core/emscripten/blob/ad438dfba750622d124f2de5b1154a374aeeaec4/src/modules.js#L270). Not really sure what's going on and whywasm0.test_longjmpworks whilewasm1doesn't, but all tests work if I moveif ASSERTIONS` into the function .

Copy link
Collaborator

Choose a reason for hiding this comment

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

You should probable move this alias into native code alongsize longjmp.

If its really just an alias you can use weak_alias in the C source code, but it looks like the signature is different so you might need to write a one-line wrapper here.

Copy link
Member Author

Choose a reason for hiding this comment

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

That sounds better. I'll split this PR into two as you suggested and do it there.

@sbc100
Copy link
Collaborator

sbc100 commented May 13, 2021

I wonder if its worth landing the "move longjmp to native code" change first.. before the wasm64 change.. since they seem logically seperate?

@aheejin
Copy link
Member Author

aheejin commented May 14, 2021

Thanks for feedback! Yeah, I'll split this PR into two smaller ones. Closing this one.

@aheejin aheejin closed this May 14, 2021
@aheejin aheejin deleted the wasm64_emsjlj branch May 14, 2021 09:56
@aheejin
Copy link
Member Author

aheejin commented May 14, 2021

The "move longjmp to native code" part is in #14188.

aheejin added a commit to llvm/llvm-project that referenced this pull request May 14, 2021
In wasm64, the signatures of some library functions and global variables
defined in Emscripten change:
- `emscripten_longjmp`: `(i32, i32) -> ()` -> `(i64, i32) -> ()`
  This changes because the first argument is the address of a memory
  buffer. This in turn causes more changes below.
- `setThrew`: `(i32, i32) -> ()` -> `(i64, i32) -> ()`
  `emscripten_longjmp` calls `setThrew` with the i64 buffer argument as
  the first parameter.
- `__THREW__` (global var): `i32` to `i64`
  `setThrew`'s first argument is set to this `__THREW__` variable, so it
  should change to i64 as well.
- `testSetjmp`: `(i32, i32*, i32) -> (i32)` -> `(i64, i32*, i32) -> (i32)`
  In the code transformation done in this pass, the value of `__THREW__`
  is passed as the first parameter of `testSetjmp`.

This patch creates some helper functions to easily get types that become
different depending on the wasm32/wasm64, and uses them to change
various function signatures and code transformations. Also updates the
tests with WASM32/WASM64 check lines.

(Untested) Emscripten side patch: emscripten-core/emscripten#14108

Reviewed By: aardappel

Differential Revision: https://reviews.llvm.org/D101985
arichardson pushed a commit to arichardson/llvm-project that referenced this pull request Sep 12, 2021
In wasm64, the signatures of some library functions and global variables
defined in Emscripten change:
- `emscripten_longjmp`: `(i32, i32) -> ()` -> `(i64, i32) -> ()`
  This changes because the first argument is the address of a memory
  buffer. This in turn causes more changes below.
- `setThrew`: `(i32, i32) -> ()` -> `(i64, i32) -> ()`
  `emscripten_longjmp` calls `setThrew` with the i64 buffer argument as
  the first parameter.
- `__THREW__` (global var): `i32` to `i64`
  `setThrew`'s first argument is set to this `__THREW__` variable, so it
  should change to i64 as well.
- `testSetjmp`: `(i32, i32*, i32) -> (i32)` -> `(i64, i32*, i32) -> (i32)`
  In the code transformation done in this pass, the value of `__THREW__`
  is passed as the first parameter of `testSetjmp`.

This patch creates some helper functions to easily get types that become
different depending on the wasm32/wasm64, and uses them to change
various function signatures and code transformations. Also updates the
tests with WASM32/WASM64 check lines.

(Untested) Emscripten side patch: emscripten-core/emscripten#14108

Reviewed By: aardappel

Differential Revision: https://reviews.llvm.org/D101985
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants