-
Couldn't load subscription status.
- Fork 222
Experimental setjmp/longjmp support #467
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
Conversation
|
the ci failure looks unrelated. i guess it's adapter/wasmtime mismatch. |
|
|
This seems quite burdensome to use--I'm not sure how much value it really has to have it upstream in this form. |
i tested the former with WebAssembly/wasi-libc#467
|
I'm a little confused because it seems like we are talking using Wasm EH here which is great. However, if we are using Wasm EH here, why the reference to Emscripten's EH (the thing that emscripten used/uses prior to Wasm EH being available)? I though that llvm has separate support for Emscripten EH and Wasm EH, but maybe I'm misunderstanding? |
Despite the pass name |
As you may have noticed, the new Wasm EH proposal that was recently changed (Oct 2023) is still not yet fully supported in the tools. LLVM currently emits the old format. But all major browsers will support the old format for the foreseeable future. V8 supports the old format by default, and the new format under the option The toolchain now has a translator in Binaryen (and you are using that here). While this translator seems working it is still very new and has not been tested and used much yet, so it can still have some bugs. And actually the name of the translator option changed yesterday to |
Ah I see. I wonder we can rename that file or move some code to avoid that confusion? |
Do you have any suggestions? So basically what it does is
|
having a working baseline would help further development at least. |
in the commit message ("It might be controversial if it's a good idea to use this emscripten API for WASI as well") i meant emscripten-looking apis like saveSetjmp getTempRet0 __wasm_longjmp. |
If its true (as I understand that it is) that those APIs are not emscripten-specific, but are part of upstream llvm support for Wasm EH, perhaps they should be renamed to match the pattern for other llvm builtins? @aheejin WDYT? |
i tested the former with WebAssembly/wasi-libc#467
If we want to upstream them, I agree we would need better names. I haven't upstreamed anything on emscripten's custom compiler-rt, but do you think we should? Apparently Wasm EH (and Emscripten EH too)'s SjLj support is not usable without those functions. Looking at LLVM's compiler-rt directory structure, I'm not sure where that would fit though.. https://github.com/llvm/llvm-project/tree/main/compiler-rt/lib |
|
@aheejin I would definitely put it under |
Basically copy-and-paste from: https://github.com/yamt/garbage/tree/master/wasm/longjmp While it seems working well, it's disabled by default because: * It might be controversial if it's a good idea to use this emscripten API for WASI as well. * LLVM produces bytecode for an old version of the EH proposal. * The EH proposal is not widely implemeted in runtimes yet. Maybe it isn't a problem for libc.a. But for libc.so, it would be a disaster for runtimes w/o EH. Tested with `binaryen --translate-eh-old-to-new` and toywasm: * build wasi-libc with BUILD_SJLJ=yes * build your app with "-mllvm -wasm-enable-sjlj" * apply "wasm-opt --translate-eh-old-to-new" * run with "toywasm --wasi" Besides that, for libc.so, your embedder needs to provide "env:__c_longjmp".
|
rebased after #468 |
|
The overall idea here looks good. C's Emscripten's particular ABI here unfortunately uses names with no prefixes.
I don't think it's a show stopper, but I also don't fully understand how the ABI works and why it's necessary to have a thread-local table rather than storing all needed info in the |
|
micropython's exception, which is using setjmp/longjmp, seems working with this. in https://github.com/micropython/micropython/tree/master/ports/embed (i haven't investigated the assertion failure because i don't expect the GC work. we don't have a way to scan some of GC roots like locals.) |
my understanding is that the table is used to track which invocation of the setjmp-calling functions jmp_buf belong to. if the last setjmp call on the buffer is not by the current invocation of the function, it rethrows the exception. it might be possible to do it without the tables. eg. save the frame pointer in jmp_buf. |
If that works, would it want a different ABI, or a different contract between compiled code and libc? |
|
I think the table is necessary. Correct me if I'm mistaken. I'm not aware of how exactly But we don't have access to PCs in Wasm and everything has to be taken care of in user space. So basically what we do in https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp is, for every function that has So we need to keep this map that maps setjmp IDs to destinations somewhere, and I'm not sure individual That's why the current library code does mallocing and reallocing. Not sure if we need a thread local struct for this as in this PR. https://github.com/emscripten-core/emscripten/blob/main/system/lib/compiler-rt/emscripten_setjmp.c is the library functions Emscripten is currently using, which I think is very similar to what this PR has here. |
|
@aheejin Each This would require the setjmp-ing function to deallocate the memory on (normal or exceptional) return, but in exchange, no |
|
@whitequark Not sure if I understand. How can I allocate a linked list on a stack using |
it would be a different ABI for sure, because the current ABI makes the user function allocate the table by calling malloc directly. |
i experimented a bit with the approach and wrote a POC implementation. |
@aheejin What I meant is that each entry of the linked list would be its own |
|
a few questions:
|
Would you mind writing up a short document describing how this approach works? That'd help us all understand it, and, since this will be part of the C ABI and relevant to compilers for other languages doing C FFI, it'd be good to have some documentation for it.
C defines |
https://docs.google.com/document/d/1ZvTPT36K5jjiedF8MCXbEmYjULJjI723aOAks1IdLLg/edit?usp=sharing
i don't understand. |
Thanks! If I understand correctly, the magic here is that there's a "setjmp id" which is an alloca address so it dynamically identifies a call frame, and it's stored in both a local variable and the
I'm not clear on what you're asking here. LLVM has to recognize at least "setjmp" because that's what users typically write. It seems like it probably could be made to recognize |
yes.
i suppose libc can have "#define setjmp __builtin_setjmp". |
i wrote a bit more complete implementation: |
|
Thank you @yamt! It's been a while since I had my LLVM reviewer hat on, but the changes look good to me. |
i'm thinking about providing a separate library (say, libsetjmp.so/a) to workaround this. |
Wasi-libc doesn't currently have any setjmp/longjmp support, so code using them doesn't work at all, no matter the engine. A setjmp/longjmp that only runs in engines with EH support would not be a regression for anyone, so I don't think a separate library is necessary. That said, EH is indeed only phase-3 and not supported in major WASI runtimes yet, so it'd be good to put something like this in setjmp.h: #ifndef __wasm_exception_handling__
#error Setjmp/longjmp support requires Exception handling support, which is [not yet standardized](https://github.com/WebAssembly/proposals?tab=readme-ov-file#phase-3---implementation-phase-cg--wg). To enable it, compile with `-mexception-handling` and use an engine that implements the Exception handling proposal.
#endif |
if we put it in libc.so, runtimes w/o EH support won't be able to load it at all. it's a regression. |
|
Ah, good point. Making it a separate library makes sense then. |
Add setjmp/longjump support based on Wasm EH proposal.
It's provided as a separate library (libsetjmp) from libc so that
runtimes w/o EH support can still load libc.so.
To use this setjmp/longjmp implementation, an application should
be compiled with `-mllvm -wasm-enable-sjlj` and linked with `-lsetjmp`.
(You need an LLVM with the change mentioned below.)
Also, you need a runtime with EH support to run such an application.
If you want to use the latest EH instructions, you can use
`binaryen --translate-eh-old-to-new` on your application.
Note: You don't need to translate libsetjmp.a/so to the new EH.
While LLVM currently produces bytecode for an old version of the EH
proposal, luckily for us, the bytecode used in this library (ie. the tag
definition and the "throw" instruction) is compatible with the latest
version of the proposal.
The runtime logic is basically copy-and-paste from:
https://github.com/yamt/garbage/tree/wasm-sjlj-alt2/wasm/longjmp
The corresponding LLVM change:
llvm/llvm-project#84137
(Note: you need this change to build setjmp/longjmp using code.
otoh, you don't need this to build libsetjmp.)
A similar change for emscripten:
emscripten-core/emscripten#21502
An older version of this PR, which doesn't require LLVM changes:
WebAssembly#467
Discussion:
https://docs.google.com/document/d/1ZvTPT36K5jjiedF8MCXbEmYjULJjI723aOAks1IdLLg/edit
An example to use the latest EH instructions:
```
clang -mllvm -wasm-enable-sjlj -o your_app.wasm your_app.c -lsetjmp
wasm-opt --translate-eh-old-to-new -o your_app.wasm your_app.wasm
toywasm --wasi your_app.wasm
```
Note: use toywasm built with `-DTOYWASM_ENABLE_WASM_EXCEPTION_HANDLING=ON`.
An example to use the older EH instructions, which LLVM currently produces:
```
clang -mllvm -wasm-enable-sjlj -o your_app.wasm your_app.c -lsetjmp
iwasm your_app.wasm
```
Note: use wasm-micro-runtime built with `-DWASM_ENABLE_EXCE_HANDLING=1`.
Note: as of writing this, only the classic interpreter supports EH.
Add setjmp/longjump support based on Wasm EH proposal.
It's provided as a separate library (libsetjmp) from libc so that
runtimes w/o EH support can still load libc.so.
To use this setjmp/longjmp implementation, an application should
be compiled with `-mllvm -wasm-enable-sjlj` and linked with `-lsetjmp`.
(You need an LLVM with the change mentioned below.)
Also, you need a runtime with EH support to run such an application.
If you want to use the latest EH instructions, you can use
`binaryen --translate-eh-old-to-new` on your application.
Note: You don't need to translate libsetjmp.a/so to the new EH.
While LLVM currently produces bytecode for an old version of the EH
proposal, luckily for us, the bytecode used in this library (ie. the tag
definition and the "throw" instruction) is compatible with the latest
version of the proposal.
The runtime logic is basically copy-and-paste from:
https://github.com/yamt/garbage/tree/wasm-sjlj-alt2/wasm/longjmp
The corresponding LLVM change:
llvm/llvm-project#84137
(Note: you need this change to build setjmp/longjmp using code.
otoh, you don't need this to build libsetjmp.)
A similar change for emscripten:
emscripten-core/emscripten#21502
An older version of this PR, which doesn't require LLVM changes:
WebAssembly#467
Discussion:
https://docs.google.com/document/d/1ZvTPT36K5jjiedF8MCXbEmYjULJjI723aOAks1IdLLg/edit
An example to use the latest EH instructions:
```
clang -mllvm -wasm-enable-sjlj -o your_app.wasm your_app.c -lsetjmp
wasm-opt --translate-eh-old-to-new -o your_app.wasm your_app.wasm
toywasm --wasi your_app.wasm
```
Note: use toywasm built with `-DTOYWASM_ENABLE_WASM_EXCEPTION_HANDLING=ON`.
An example to use the older EH instructions, which LLVM currently produces:
```
clang -mllvm -wasm-enable-sjlj -o your_app.wasm your_app.c -lsetjmp
iwasm your_app.wasm
```
Note: use wasm-micro-runtime built with `-DWAMR_BUILD_EXCE_HANDLING=1`.
Note: as of writing this, only the classic interpreter supports EH.
* Add libsetjmp.a/so
Add setjmp/longjump support based on Wasm EH proposal.
It's provided as a separate library (libsetjmp) from libc so that
runtimes w/o EH support can still load libc.so.
To use this setjmp/longjmp implementation, an application should
be compiled with `-mllvm -wasm-enable-sjlj` and linked with `-lsetjmp`.
(You need an LLVM with the change mentioned below.)
Also, you need a runtime with EH support to run such an application.
If you want to use the latest EH instructions, you can use
`binaryen --translate-eh-old-to-new` on your application.
Note: You don't need to translate libsetjmp.a/so to the new EH.
While LLVM currently produces bytecode for an old version of the EH
proposal, luckily for us, the bytecode used in this library (ie. the tag
definition and the "throw" instruction) is compatible with the latest
version of the proposal.
The runtime logic is basically copy-and-paste from:
https://github.com/yamt/garbage/tree/wasm-sjlj-alt2/wasm/longjmp
The corresponding LLVM change:
llvm/llvm-project#84137
(Note: you need this change to build setjmp/longjmp using code.
otoh, you don't need this to build libsetjmp.)
A similar change for emscripten:
emscripten-core/emscripten#21502
An older version of this PR, which doesn't require LLVM changes:
#467
Discussion:
https://docs.google.com/document/d/1ZvTPT36K5jjiedF8MCXbEmYjULJjI723aOAks1IdLLg/edit
An example to use the latest EH instructions:
```
clang -mllvm -wasm-enable-sjlj -o your_app.wasm your_app.c -lsetjmp
wasm-opt --translate-eh-old-to-new -o your_app.wasm your_app.wasm
toywasm --wasi your_app.wasm
```
Note: use toywasm built with `-DTOYWASM_ENABLE_WASM_EXCEPTION_HANDLING=ON`.
An example to use the older EH instructions, which LLVM currently produces:
```
clang -mllvm -wasm-enable-sjlj -o your_app.wasm your_app.c -lsetjmp
iwasm your_app.wasm
```
Note: use wasm-micro-runtime built with `-DWAMR_BUILD_EXCE_HANDLING=1`.
Note: as of writing this, only the classic interpreter supports EH.
* Make libsetjmp build optional
* CI: Disable libsetjmp for old LLVM
* libc-top-half/musl/include/setjmp.h: fix a rebase botch
|
Closing as this is now subsumed by #483. |
Basically copy-and-paste from:
https://github.com/yamt/garbage/tree/master/wasm/longjmp
While it seems working well, it's disabled by default because:
It might be controversial if it's a good idea to use this emscripten API for WASI as well.
LLVM produces bytecode for an old version of the EH proposal.
The EH proposal is not widely implemeted in runtimes yet. Maybe it isn't a problem for libc.a. But for libc.so, it would be a disaster for runtimes w/o EH.
Tested with
binaryen --translate-eh-old-to-newand toywasm:Besides that, for libc.so, your embedder needs to provide "env:__c_longjmp".