Skip to content

Simplify exception handling on wasm - #160067

Open
bjorn3 wants to merge 3 commits into
rust-lang:mainfrom
bjorn3:refactor_unwind3
Open

Simplify exception handling on wasm#160067
bjorn3 wants to merge 3 commits into
rust-lang:mainfrom
bjorn3:refactor_unwind3

Conversation

@bjorn3

@bjorn3 bjorn3 commented Jul 28, 2026

Copy link
Copy Markdown
Member
  • Fix _Unwind_Exception size on Emscripten.
  • Inline _Unwind_DeleteException on all targets.
  • Use llvm.wasm.throw on all wasm targets.

Follow up to #159785

bjorn3 added 3 commits July 27, 2026 23:44
The 20 word requirement was likely from the JS EH ABI which we no longer
support.
I can't think of any reason why any implementation of
_Unwind_DeleteException would do anything different. This reduces the
amount of special casing in the unwind crate for wasm32-unknown-unknown.
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 28, 2026
@rustbot

rustbot commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

r? @jhpratt

rustbot has assigned @jhpratt.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: libs
  • libs expanded to 12 candidates
  • Random selection from 6 candidates

@bjorn3

bjorn3 commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

@aheejin Is it safe to always use llvm.wasm.throw on all wasm targets including Emscripten? Or is it possible that a future version of libunwind will have a different _Unwind_RaiseException definition that is incompatible with directly throwing a wasm exception using llvm.wasm.throw?

cc @hoodmane

@aheejin

aheejin commented Jul 29, 2026

Copy link
Copy Markdown

I'm not sure if I understand. llvm.wasm.throw translates directly to a Wasm throw instruction. It takes a tag index and a value to throw. I don't see why that's target dependent? Also our _Unwind_RaiseException in libunwind just calls a clang builtin that is lowered down to llvm.wasm.throw intrinsic later:
https://github.com/llvm/llvm-project/blob/b3607548955c1ad318ea5b2019ea95b64c93ebe8/libunwind/src/Unwind-wasm.c#L63-L70
The only thing that can be platform dependent is the tag index (0) which we assigned for C++ in LLVM. But because Rust has its own libunwind equivalent, I think it can use another index.

@bjorn3

bjorn3 commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

Also our _Unwind_RaiseException in libunwind just calls a clang builtin that is lowered down to llvm.wasm.throw intrinsic later:

I'm aware. Is this a stable guarantee though? Or is it possible that a future libunwind version will do something else that would make us using llvm.wasm.throw directly be ABI incompatible with C++ code using libunwind _Unwind_RaiseException? For example storing a pointer to the exception in a global or setting an "unwinding" flag.

The only thing that can be platform dependent is the tag index (0) which we assigned for C++ in LLVM. But because Rust has its own libunwind equivalent, I think it can use another index.

LLVM unfortunately doesn't support using arbitrary execption tags afaik and even if it did, I don't know if catch(...) on the C++ side would still catch the exception (and preferably abort) or if it will silently unwind through. Also on wasi and emscripten we do in fact use upstream libunwind before this PR. After this PR we only depend on it for the __cpp_exception tag.

@jhpratt

jhpratt commented Jul 29, 2026

Copy link
Copy Markdown
Member

I'm unfamiliar with the requirements of this class of targets. If you know of someone that may be familiar, I'd suggest requesting them as the reviewer. Otherwise, @rustbot reroll will let someone else handle it.

@hoodmane

hoodmane commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@alexcrichton seems like the obvious choice for reviewer if he has bandwidth. @workingjubilee and @petrochenkov have also reviewed my PRs in this general area in the past.

Comment on lines -89 to +91
uw::_Unwind_DeleteException(exception);
if let Some(exception_cleanup) = (*exception).exception_cleanup {
exception_cleanup(uw::_URC_FOREIGN_EXCEPTION_CAUGHT, exception);
}

@alexcrichton alexcrichton Jul 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Given the breadth of libunwind implementations, how confident are you that all implementations basically look like this? I'd expect some libunwind somewhere to do something weird for example which means that this might be a regression.

View changes since the review

Comment thread library/unwind/src/lib.rs
target_os = "psp",
target_os = "solid_asp3",
all(target_vendor = "fortanix", target_env = "sgx"),
all(target_os = "wasi", panic = "unwind"),

@alexcrichton alexcrichton Jul 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Personally I feel it'll work best to "pretend like you're native" on WASI as opposed to having wasm-specific code here. You've already got concerns about possible future LLVM changes/evolutions, for example, and presumably libunwind would be kept in sync which means Rust wouldn't have to explicitly keep in sync.

Have you tested this on WASI targets to see if it works? I remember dealing with __cpp_exception is finnicky and difficult to precisely align.

View changes since the review

@bjorn3 bjorn3 Jul 29, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

WASI is already not native in that we don't have a personality function on wasm and consequently all functions that the personality function would normally need to call are entirely missing on wasm.

You've already got concerns about possible future LLVM changes/evolutions, for example, and presumably libunwind would be kept in sync which means Rust wouldn't have to explicitly keep in sync.

We already would need to adapt to LLVM changes on wasm32-unknown-unknown. This PR merely makes that code used on all wasm targets.

Have you tested this on WASI targets to see if it works?

Not yet.

I remember dealing with __cpp_exception is finnicky and difficult to precisely align.

It should still be using __cpp_exception from libunwind.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd recommend testing this change. "Should work" I have found often means "there's some subtle reason that this doesn't actually work", especially when it comes to things like this.

For WASI libunwind is already linked, so I think it's best to use it directly instead of going behind it to throw an exception.

@aheejin

aheejin commented Jul 29, 2026

Copy link
Copy Markdown

Also our _Unwind_RaiseException in libunwind just calls a clang builtin that is lowered down to llvm.wasm.throw intrinsic later:

I'm aware. Is this a stable guarantee though? Or is it possible that a future libunwind version will do something else that would make us using llvm.wasm.throw directly be ABI incompatible with C++ code using libunwind _Unwind_RaiseException? For example storing a pointer to the exception in a global or setting an "unwinding" flag.

The only thing that can be platform dependent is the tag index (0) which we assigned for C++ in LLVM. But because Rust has its own libunwind equivalent, I think it can use another index.

LLVM unfortunately doesn't support using arbitrary execption tags afaik and even if it did, I don't know if catch(...) on the C++ side would still catch the exception (and preferably abort) or if it will silently unwind through. Also on wasi and emscripten we do in fact use upstream libunwind before this PR. After this PR we only depend on it for the __cpp_exception tag.

I'm not sure if I understand Rust's configuration correctly. After this PR, if you are not going to use _Unwind_RaiseException, why are you concerned?

And how much of LLVM C/C++ libraries is the Rust EH using?

As per _Unwind_RaiseException, currently I can't think of any reason to change the status quo. But I haven't thought it as in terms of the API's guarantee.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants