wasi-sdk icon indicating copy to clipboard operation
wasi-sdk copied to clipboard

Add Support for WASM Exceptions

Open woodsmc opened this issue 1 year ago • 21 comments

There are plans to add support for exceptions in WAMR. Are there any plans to add support to the WASI-SDK tool chain to support Native WASM Exceptions?

(edit: fix link)

woodsmc avatar Jul 06 '23 21:07 woodsmc

The rough plan is to look at Emscripten's port of libunwind to see if it can be either ported to upstream LLVM, or if not, ported to wasi-sdk. I don't know of anyone working on this yet.

It's also worth noting that Wasm Exceptions are not yet standardized; currently the proposal is in phase 3.

sunfishcode avatar Jul 06 '23 21:07 sunfishcode

We hope to have an initial contribution to WAMR with exception support in the next few months. There is such a swathe of C++ software in-particular that is excluded from the WASM ecosystem because of the lack of exception support. It would be awesome to see a solution.

Although, digging through the code that is generated by the emscripten tool chain we can see, that at least for C++ it generates a single WASM exception throw and catch. This mimics almost exactly the approach used by traditional C++ compilers, but it means that the majority of the, quite elegant and nuanced behavior described in the WASM Exception proposal is rarely used. - This might be different for other programming languages - I've not checked.

woodsmc avatar Jul 07 '23 12:07 woodsmc

As Dan mentioned, the easy part is enabling exceptions in the compiler (e.g. passing -fwasm-exceptions). The more compex/nuanced part is getting libc++abi/libunwind configured correctly.

To see that current differences from upstream that were made in emscripten you can take a look here: https://github.com/emscripten-core/emscripten/blob/main/system/lib/libcxxabi/readme.txt. Specifically check out the link at the end that shows you the diffs from upstream llvm 16: https://github.com/llvm/llvm-project/compare/llvmorg-16.0.4...emscripten-core:emscripten-libs-16

sbc100 avatar Jul 07 '23 15:07 sbc100

Whooha... Rockstar's both of you, and I've got some reading to do. Thanks for the pointers. Let me take a look.

woodsmc avatar Jul 07 '23 18:07 woodsmc

There's also a draft PR with initial support for this in #198, which you may be interested in.

abrown avatar Jul 25 '23 16:07 abrown

I have a plan to upstream the current libc++abi/libunwind changes to the upstream LLVM. I think it can happen within a couple months if having this is a priority; the timeline can depend on the receptions from the reviewers though.

By the way there is a discussions going on (https://github.com/WebAssembly/exception-handling/issues/280) which can change the spec itself. I don't think the library side will change much with this though.

aheejin avatar Aug 03 '23 02:08 aheejin

Thank you for the wait. Wasm EH's libcxxabi/libunwind changes have now been upstreamed: https://github.com/llvm/llvm-project/commit/e6cbba749490a20127359a3fbd05d8db7faef535 https://github.com/llvm/llvm-project/commit/058222b2316615194c089f2bc68d11341f39d26e

aheejin avatar Sep 22 '23 07:09 aheejin

What does -fwasm-exceptions do? Adding or removing that makes no difference, I still end up with

wasm-ld: error: /var/folders/74/djsf8lv965l2tkgr29ybpz4h0000gn/T/lib-858918.o: undefined symbol: __cxa_allocate_exception
wasm-ld: error: /var/folders/74/djsf8lv965l2tkgr29ybpz4h0000gn/T/lib-858918.o: undefined symbol: __cxa_free_exception
wasm-ld: error: /var/folders/74/djsf8lv965l2tkgr29ybpz4h0000gn/T/lib-858918.o: undefined symbol: __cxa_throw

thx

sroussey avatar Jan 06 '24 01:01 sroussey

Just wanted to check, would WASM exceptions make sense for implementing C setjmp / longjmp (i.e., how postgres uses them to implement exceptions), or are they too different and only make sense with language-native exceptions like in C++? If the latter, is there currently no plan that would be able to support setjmp / longjmp in WASI?

anuraaga avatar Jan 22 '24 07:01 anuraaga

I think you should be able to lower SjLj to exceptions.

whitequark avatar Jan 22 '24 10:01 whitequark

Indeed, llvm already uses wasm exception to implement SJLJ, when wasm exceptions are enabled, so it should just work once we enable exceptions.

sbc100 avatar Jan 22 '24 17:01 sbc100

Indeed, wasm exceptions is how we've always envisioned setjmp/longjmp would work.

sunfishcode avatar Jan 22 '24 18:01 sunfishcode

Thanks for confirming! I implemented stack unwind for wazero

https://github.com/tetratelabs/wazero/pull/1808

and look forward to trying it out with wasi SDK (from my reading of the thread it isn't quite ready yet for use with the published SDKs)

anuraaga avatar Jan 23 '24 07:01 anuraaga

What does -fwasm-exceptions do? Adding or removing that makes no difference, I still end up with

wasm-ld: error: /var/folders/74/djsf8lv965l2tkgr29ybpz4h0000gn/T/lib-858918.o: undefined symbol: __cxa_allocate_exception
wasm-ld: error: /var/folders/74/djsf8lv965l2tkgr29ybpz4h0000gn/T/lib-858918.o: undefined symbol: __cxa_free_exception
wasm-ld: error: /var/folders/74/djsf8lv965l2tkgr29ybpz4h0000gn/T/lib-858918.o: undefined symbol: __cxa_throw

thx

I found that llvm 17 was branched off end of June and above exception patches were merged mid September, so we need to wait for llvm 18 (should branch off today, first RC on Friday) or upgrade to a git version.

cpetig avatar Jan 23 '24 17:01 cpetig

The -fwasm-exceptions flag allows the compiler to output the try / catch and other exception handling instructions in WebAssembly bytecode. But it is up to the language implementation to determine how these instructions are used.

Languages aren't used to dealing with WASM exception instructions. Instead they traditionally use setjmp / longjmp, C++ is a good example of this. Therefore the language's supporting library (also confusingly called the C++ runtime) needs to be updated to use exception handling.

When the C++ compiler encounters an exception in the code you are attempting to compile, behind the scenes it generates calls to it's runtime library to implement the exception handling. It is inside the runtime library that the exception instructions are used.

The error your seeing is an error generated by WASI-SDK, which bundles the C++ runtime library, the version of the library supplied by WASI-SDK doesn't include the three functions listed above - which are all designed for exception handling.

So when you compile C++ with an exception in your code, the compiler generates it faithfully, with calls to functions in the C++ runtime library which do not exist, this is why, at link time you get the error message you mentioned.

The Emscripten folks have worked on this and have an update which includes the C++ runtime functions we're missing. I understand they were hoping to upstream this to LLVM, and then WASI-SDK would be able to pull this down. - I'm not sure of the current state of this work, @sunfishcode and @aheejin and, I think the best folks to be able to help on that front.

woodsmc avatar Jan 25 '24 21:01 woodsmc

That is a great explanation, thank you.

sroussey avatar Jan 26 '24 16:01 sroussey

The library code has been already upstreamed, as I commented in https://github.com/WebAssembly/wasi-sdk/issues/334#issuecomment-1730976179. But given that Emscripten uses our custom build system and not the LLVM's CMake build system, how it works well with the native LLVM has not been well tested and you might need some adjustment on the build files. I'm not familiar with what build system WASI uses (Is that the LLVM's or something separate?), but that will require changes as well. For example, from libunwind we only use Unwind-wasm.c and nothing else: https://github.com/emscripten-core/emscripten/blob/4aefde3f78e0d3d6327105b123c99dbe3283f779/tools/system_libs.py#L1635

aheejin avatar Jan 30 '24 03:01 aheejin

Indeed, llvm already uses wasm exception to implement SJLJ, when wasm exceptions are enabled, so it should just work once we enable exceptions.

do you mean to use saveSetjmp getTempRet0 etc for WASI as well?

yamt avatar Jan 30 '24 09:01 yamt

Indeed, llvm already uses wasm exception to implement SJLJ, when wasm exceptions are enabled, so it should just work once we enable exceptions.

do you mean to use saveSetjmp getTempRet0 etc for WASI as well?

Right, and this seems to have been moved to compiler-rt in Emscripten: https://github.com/emscripten-core/emscripten/blob/main/system/lib/compiler-rt/emscripten_setjmp.c

Even though the file name says 'emscripten', some of the functions in there are also used by Wasm SjLj handling (which is, SjLj handling using the instructions in the Wasm EH proposal) That file was newly added in Emscripten and didn't come from the compiler-rt in LLVM.

I understand this is getting confusing somewhat, which I think stemmed from that Emscipten uses its custom build system.. I guess we should consider making LLVM's CMake system to reflect the Emscripten setup. In the meantime, this is the list of library files created/modified for Wasm EH in Emscripten:

compiler-rt: https://github.com/emscripten-core/emscripten/blob/main/system/lib/compiler-rt/emscripten_setjmp.c (created)

libc++abi: https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_personality.cpp (modified) https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_exception.cpp (modified) https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_exception.h (modified)

libunwind: https://github.com/llvm/llvm-project/blob/main/libunwind/src/Unwind-wasm.c (created)

aheejin avatar Jan 30 '24 21:01 aheejin

Indeed, llvm already uses wasm exception to implement SJLJ, when wasm exceptions are enabled, so it should just work once we enable exceptions.

do you mean to use saveSetjmp getTempRet0 etc for WASI as well?

Right, and this seems to have been moved to compiler-rt in Emscripten: https://github.com/emscripten-core/emscripten/blob/main/system/lib/compiler-rt/emscripten_setjmp.c

Even though the file name says 'emscripten', some of the functions in there are also used by Wasm SjLj handling (which is, SjLj handling using the instructions in the Wasm EH proposal) That file was newly added in Emscripten and didn't come from the compiler-rt in LLVM.

I understand this is getting confusing somewhat, which I think stemmed from that Emscipten uses its custom build system.. I guess we should consider making LLVM's CMake system to reflect the Emscripten setup. In the meantime, this is the list of library files created/modified for Wasm EH in Emscripten:

compiler-rt: https://github.com/emscripten-core/emscripten/blob/main/system/lib/compiler-rt/emscripten_setjmp.c (created)

libc++abi: https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_personality.cpp (modified) https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_exception.cpp (modified) https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_exception.h (modified)

libunwind: https://github.com/emscripten-core/emscripten/blob/main/system/lib/libunwind/src/Unwind-wasm.c (created)

thank you. i did an experiment to use sjlj with wasi. it seems working well. https://github.com/yamt/garbage/tree/master/wasm/longjmp (i wrote the runtime (rt.c) by myself because i didn't know where emscripten version was.)

yamt avatar Jan 31 '24 03:01 yamt

https://github.com/WebAssembly/wasi-libc/pull/467

yamt avatar Jan 31 '24 09:01 yamt