This is an archive of the discontinued LLVM Phabricator instance.

[WebAssembly] Tidy up EH/SjLj options
ClosedPublic

Authored by aheejin on Aug 6 2021, 9:06 PM.

Details

Summary

This CL is small, but the description can be a little long because I'm
trying to sum up the status quo for Emscripten/Wasm EH/SjLj options.

First, this CL adds an option for Wasm SjLj (-wasm-enable-sjlj), which
handles SjLj using Wasm EH. The implementation for this will be added as
a followup CL, but this adds the option first to do error checking.

Now we have two modes of EH (Emscripten/Wasm) and also two modes of SjLj
(also Emscripten/Wasm). The options corresponding to each of are:

  • Emscripten EH: -enable-emscripten-cxx-exceptions
  • Emscripten SjLj: -enable-emscripten-sjlj
  • Wasm EH: -exception-model=wasm -mattr=+exception-handling
  • Wasm SjLj: -wasm-enable-sjlj -exception-model=wasm -mattr=+exception-handling

The reason Wasm EH/SjLj's options are a little complicated are
-exception-model and -mattr are common LLVM options ane not under
our control. (-mattr can be omitted if it is embedded within the
bitcode file.)

And we have the following rules of the option composition:

  • Emscripten EH and Wasm EH cannot be turned on at the same itme
  • Emscripten SjLj and Wasm SjLj cannot be turned on at the same time
  • Wasm SjLj should be used with Wasm EH

Which means we now allow these combinations:

  • Emscripten EH + Emscripten SjLj: the current default in emcc
  • Wasm EH + Emscripten SjLj This is allowed, but only as an interim step in which we are testing Wasm EH but not yet have a working implementation of Wasm SjLj. This will error out (D107687) in compile time if setjmp is called in a function in which Wasm exception is used.
  • Wasm EH + Wasm SjLj This will be the default mode later when using Wasm EH. Currently Wasm SjLj implementation doesn't exist, so it doesn't work.
  • Emscripten EH + Wasm SjLj will not work.

This CL moves these error checking routines to
WebAssemblyPassConfig::addIRPasses. Not sure if this is an ideal place
to do this, but I couldn't find elsewhere. Currently some checking is
done within LowerEmscriptenEHSjLj, but these checks only run if
LowerEmscriptenEHSjLj runs so it may not run when Wasm EH is used. This
moves that to addIRPasses and adds some more checks.

Currently LowerEmscriptenEHSjLj pass is responsible for Emscripten EH
and Emscripten SjLj. Wasm EH transformations are done in multiple
places, including WasmEHPrepare, LateEHPrepare, and CFGStackify. But in
the followup CL, LowerEmscriptenEHSjLj pass will be also responsible for
a part of Wasm SjLj transformation, because WasmSjLj will also be using
several Emscripten library functions, and we will be sharing more than
half of the transformation to do that between Emscripten SjLj and Wasm
SjLj.

Currently we have -enable-emscripten-cxx-exceptions and
-enable-emscripten-sjlj but these only work for llc, because for
llc we feed these options to the pass but when we run the pass using
opt the pass will be created with no options and the default options
will be used, which turns both Emscripten EH and Emscripten SjLj on.

Now we have one more SjLj option to care for, LowerEmscriptenEHSjLj pass
needs a finer way to control these options. This CL removes those
default parameters and make LowerEmscriptenEHSjLj pass read directly
from command line options specified. So if we only run
opt -wasm-lower-em-ehsjlj, currently both Emscripten EH and Emscripten
SjLj will run, but with this CL, none will run unless we additionally
pass -enable-emscripten-cxx-exceptions or -enable-emscripten-sjlj,
or both. This does not affect users; this only affects our opt tests
because emcc will not call either opt or llc. As a result of this,
our existing Emscripten EH/SjLj tests gained one or both of those
options in their RUN lines.


Edit: We later decided to also add -wasm-enable-eh, and the commit
message will be accordingly modified. I leave this CL description as is
to maintain conversation history.

Diff Detail

Event Timeline

aheejin created this revision.Aug 6 2021, 9:06 PM
aheejin requested review of this revision.Aug 6 2021, 9:06 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 6 2021, 9:06 PM
aheejin edited the summary of this revision. (Show Details)Aug 6 2021, 9:09 PM
aheejin edited the summary of this revision. (Show Details)
aheejin edited the summary of this revision. (Show Details)Aug 9 2021, 11:08 AM

I get that enabling the new SjLj will require enabling the wasm EH feature, but it's not obvious why it should require --exception-model=wasm Is it possible to enable the new SJLJ with exception-model=none and have it force on -mattr=+exception-handling (or failing that, require only -mattr=+exception-handling and not both)? I would think e.g. this should eventually be the default for plain C code.

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
435
aheejin marked an inline comment as done.Aug 12 2021, 6:29 PM
aheejin updated this revision to Diff 366163.Aug 12 2021, 6:29 PM

Fix a typo

I get that enabling the new SjLj will require enabling the wasm EH feature, but it's not obvious why it should require --exception-model=wasm Is it possible to enable the new SJLJ with exception-model=none and have it force on -mattr=+exception-handling (or failing that, require only -mattr=+exception-handling and not both)? I would think e.g. this should eventually be the default for plain C code.

Hmm, I've been thinking about this and I don't quite like the current candidates I proposed either. For example, what if someone wants to disable exceptions, i.e., lower all invokes to calls, but want to handle SjLj using Wasm EH instructions? The current options don't allow that, because -wasm-enable-sjlj requires -exception-model=wasm, and -exception-model=wasm means we enable Wasm EH.

I feel -exception-model=wasm should be treated more like -mattr=+exception-handling, which means, the capability of handling wasm EH instructions. We use it with that meaning already in various parts of codebase. For example in WebAssembly target:
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp#L143-L144
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp#L119-L121
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp#L1566-L1568

But also in common code:
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/TargetPassConfig.cpp#L920-L926
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp#L412
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp#L413-L415

In all these place, what they check is not whether we have lowered our invokes into calls or anything, but rather "Do we have Wasm EH instructions that need to be supported?" And the answer should be yes when we use Wasm EH instructions for SjLj too. It is hard to replace this with -mattr=+exception-handling, because, -exception-model= is not used this way only by us but by common code too, and I don't think the -mattr= flag is for that purpose in the first place. It simply means the current architecture is new enough to support the feature, and it doesn't mean we should use Wasm EH for anything.

So, how about adding a new option, let's say, -wasm-enable-eh, which is a parallel of -wasm-enable-sjlj? This still will only affect opt and llc and it will be set appropriately by clang flags which will be controlled by emcc, so it doesn't mean we require users to pass an additional flag by doing this. -exception-model=wasm and -mattr=+exception-handling mean just the capability of our backend that we can handle Wasm EH instructions in case they are used anywhere in the module. If you use either of -wasm-enable-eh or -wasm-enable-sjlj, both of -exception-model=wasm and -mattr=+exception-handling should be present. (`-mattr=+exception-handling can be omitted if it is in the bitcode file attributes, which will be the case in most cases)

Even with both of -exception-model=wasm and -mattr=+exception-handling present, we can choose to handle neither of exceptions or SjLj or only one of them, or both. The list of options required for each mode will be:

  • Emscripten EH: -enable-emscripten-cxx-exceptions
  • Emscripten SjLj: -enable-emscripten-sjlj
  • Wasm EH: -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling
  • Wasm SjLj: -wasm-enable-sjlj -exception-model=wasm -mattr=+exception-handling

What do you think?

In D107685#2942894, @aheejin wrote:

Hmm, I've been thinking about this and I don't quite like the current candidates I proposed either. For example, what if someone wants to disable exceptions, i.e., lower all invokes to calls, but want to handle SjLj using Wasm EH instructions? The current options don't allow that, because -wasm-enable-sjlj requires -exception-model=wasm, and -exception-model=wasm means we enable Wasm EH.

This case of "disable exceptions but use wasm EH for SjLj" is the case I was thinking about too, and I do think we should support that. ​There is one nuance here though about lowering invokes. The traditional emscripten way to disable exceptions is to generate the code with invokes and then lower them away. But the upstream way is to use -fignore-exceptions which IIUC does not do that; instead it just does not generate any invokes at all (see e.g. clang/test/CodeGen/ignore-exceptions.cpp). And of course C does not generate any invokes.
Although, looking at your examples below, I guess that distinction doesn't actually matter in this case.

I feel -exception-model=wasm should be treated more like -mattr=+exception-handling, which means, the capability of handling wasm EH instructions. We use it with that meaning already in various parts of codebase. For example in WebAssembly target:
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp#L143-L144
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp#L119-L121
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp#L1566-L1568

But also in common code:
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/TargetPassConfig.cpp#L920-L926
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp#L412
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp#L413-L415

In all these place, what they check is not whether we have lowered our invokes into calls or anything, but rather "Do we have Wasm EH instructions that need to be supported?" And the answer should be yes when we use Wasm EH instructions for SjLj too. It is hard to replace this with -mattr=+exception-handling, because, -exception-model= is not used this way only by us but by common code too, and I don't think the -mattr= flag is for that purpose in the first place. It simply means the current architecture is new enough to support the feature, and it doesn't mean we should use Wasm EH for anything.

My intuition is that -exception-model should really only affect C++ exceptions and not be a generic flag. But because it seems to be an LLVM-internal thing (as opposed to a user-facing clang flag) I guess I'm not that surprised that the usage isn't purely for that.
I would actually be OK with using -mattr to mean "use wasm EH for setjmp/longjmp". With other attributes (and I think even on other targets), AFAIK it doesn't just mean that the target is new enough, but it also causes codegen to actually generate the instructions as well?
I suppose you could make the argument that it should be possible to enable EH instructions but disable SJLJ lowering (in that case, what would you do? I guess you'd just have to lower setjmp/longjmp to call an import?)

So, how about adding a new option, let's say, -wasm-enable-eh, which is a parallel of -wasm-enable-sjlj? This still will only affect opt and llc and it will be set appropriately by clang flags which will be controlled by emcc, so it doesn't mean we require users to pass an additional flag by doing this. -exception-model=wasm and -mattr=+exception-handling mean just the capability of our backend that we can handle Wasm EH instructions in case they are used anywhere in the module. If you use either of -wasm-enable-eh or -wasm-enable-sjlj, both of -exception-model=wasm and -mattr=+exception-handling should be present. (`-mattr=+exception-handling can be omitted if it is in the bitcode file attributes, which will be the case in most cases)

Even with both of -exception-model=wasm and -mattr=+exception-handling present, we can choose to handle neither of exceptions or SjLj or only one of them, or both. The list of options required for each mode will be:

  • Emscripten EH: -enable-emscripten-cxx-exceptions
  • Emscripten SjLj: -enable-emscripten-sjlj
  • Wasm EH: -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling
  • Wasm SjLj: -wasm-enable-sjlj -exception-model=wasm -mattr=+exception-handling

What do you think?

So -exception-model=wasm without either -wasm-enable-eh or -wasm-enable-sjlj would be an error, or a no-op? And either -wasm-enable-eh or -wasm-enable-sjlj without -exception-model-wasm would be an error?
I guess that would be ok as long as we have reasonable errors (clang users wouldn't need to care of course, but Rust or other users of just LLVM might still want to be able to use the capability of setjmp/longjmp without having to generate full WinEH-style IR)

aheejin added a comment.EditedAug 13 2021, 3:09 PM

In D107685#2942894, @aheejin wrote:

Hmm, I've been thinking about this and I don't quite like the current candidates I proposed either. For example, what if someone wants to disable exceptions, i.e., lower all invokes to calls, but want to handle SjLj using Wasm EH instructions? The current options don't allow that, because -wasm-enable-sjlj requires -exception-model=wasm, and -exception-model=wasm means we enable Wasm EH.

This case of "disable exceptions but use wasm EH for SjLj" is the case I was thinking about too, and I do think we should support that. ​There is one nuance here though about lowering invokes. The traditional emscripten way to disable exceptions is to generate the code with invokes and then lower them away. But the upstream way is to use -fignore-exceptions which IIUC does not do that; instead it just does not generate any invokes at all (see e.g. clang/test/CodeGen/ignore-exceptions.cpp). And of course C does not generate any invokes.
Although, looking at your examples below, I guess that distinction doesn't actually matter in this case.

I feel -exception-model=wasm should be treated more like -mattr=+exception-handling, which means, the capability of handling wasm EH instructions. We use it with that meaning already in various parts of codebase. For example in WebAssembly target:
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp#L143-L144
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp#L119-L121
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp#L1566-L1568

But also in common code:
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/TargetPassConfig.cpp#L920-L926
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp#L412
https://github.com/llvm/llvm-project/blob/76beb4184cfcf41cb60fbcfb836b41eb58321b32/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp#L413-L415

In all these place, what they check is not whether we have lowered our invokes into calls or anything, but rather "Do we have Wasm EH instructions that need to be supported?" And the answer should be yes when we use Wasm EH instructions for SjLj too. It is hard to replace this with -mattr=+exception-handling, because, -exception-model= is not used this way only by us but by common code too, and I don't think the -mattr= flag is for that purpose in the first place. It simply means the current architecture is new enough to support the feature, and it doesn't mean we should use Wasm EH for anything.

My intuition is that -exception-model should really only affect C++ exceptions and not be a generic flag. But because it seems to be an LLVM-internal thing (as opposed to a user-facing clang flag) I guess I'm not that surprised that the usage isn't purely for that.

I think the current LLVM usages are more for "any exceptions" and not only meant for C++ exceptions, meaning, anything that can generate invokes. Clang can compile Objective C, and I guess it will apply to other frontends of LLVM if they generate invoke.

I would actually be OK with using -mattr to mean "use wasm EH for setjmp/longjmp". With other attributes (and I think even on other targets), AFAIK it doesn't just mean that the target is new enough, but it also causes codegen to actually generate the instructions as well?

I think this is a valid point of view. I was mostly thinking it would be better to give a choice even if we are running on a brand-new engine that support all proposed features, in the same way we let users use Emscripten EH even if the engine and toolchain support Wasm EH. But this is also necessary as an interim measure while Wasm EH support is not very stable.

I suppose you could make the argument that it should be possible to enable EH instructions but disable SJLJ lowering (in that case, what would you do? I guess you'd just have to lower setjmp/longjmp to call an import?)

If we disable SjLj lowering entirely, they will error out at link time or something because we don't have those functions in Emscripten library. The current default is to use Emscripten SjLj.

So, how about adding a new option, let's say, -wasm-enable-eh, which is a parallel of -wasm-enable-sjlj? This still will only affect opt and llc and it will be set appropriately by clang flags which will be controlled by emcc, so it doesn't mean we require users to pass an additional flag by doing this. -exception-model=wasm and -mattr=+exception-handling mean just the capability of our backend that we can handle Wasm EH instructions in case they are used anywhere in the module. If you use either of -wasm-enable-eh or -wasm-enable-sjlj, both of -exception-model=wasm and -mattr=+exception-handling should be present. (`-mattr=+exception-handling can be omitted if it is in the bitcode file attributes, which will be the case in most cases)

Even with both of -exception-model=wasm and -mattr=+exception-handling present, we can choose to handle neither of exceptions or SjLj or only one of them, or both. The list of options required for each mode will be:

  • Emscripten EH: -enable-emscripten-cxx-exceptions
  • Emscripten SjLj: -enable-emscripten-sjlj
  • Wasm EH: -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling
  • Wasm SjLj: -wasm-enable-sjlj -exception-model=wasm -mattr=+exception-handling

What do you think?

So -exception-model=wasm without either -wasm-enable-eh or -wasm-enable-sjlj would be an error, or a no-op?

I think it would error out, which I think is clearer. Also note that these are not user-facing flags so if they are inconsistent it is more likely to be an error from the driver side.

And either -wasm-enable-eh or -wasm-enable-sjlj without -exception-model-wasm would be an error?

I think so too.

I guess that would be ok as long as we have reasonable errors (clang users wouldn't need to care of course, but Rust or other users of just LLVM might still want to be able to use the capability of setjmp/longjmp without having to generate full WinEH-style IR)

I still support -wasm-enable-eh as a parallel of -wasm-enable-sjlj; I think it is less confusing. Later, if we think everyone should switch to Wasm EH/SjLj and refrain from using Emscripten EH/SjLj, we can make the default values of -wasm-enable-sjlj and -wasm-enable-eh true. These -wasm-enable-*** flags will only be used to determine how to lower invokes and setjmp/longjmps in LowerEmscriptenEHSjLJ and WasmEHPrepare passes, and our later backend passes checks the exception model only without caring whether EH instructions were generated from exceptions or SjLj.

OK, yeah that sounds good to me.

aheejin updated this revision to Diff 368203.Aug 23 2021, 1:37 PM

Add -wasm-enable-eh

Herald added a project: Restricted Project. · View Herald TranscriptAug 23 2021, 1:37 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
dschuff accepted this revision.Aug 23 2021, 3:54 PM
dschuff added inline comments.
llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
278

You could put the comment text directly in the assert, i.e. assert(condition && "text")

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
48

this description seems a bit redundant. I guess "C++ exception handling using wasm EH instructions" might be a bit too specific since it could be anything that generates WinEH-style IR. Maybe just leave out the first "wasm"?

449

it's not clear what "and processed" is intended to mean here.

This revision is now accepted and ready to land.Aug 23 2021, 3:54 PM
aheejin updated this revision to Diff 368237.Aug 23 2021, 4:33 PM
aheejin marked 3 inline comments as done.

Address comments

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
449

Yeah the sentence is unclear and even grammatically incorrect... Rewrote it.

dschuff accepted this revision.Aug 24 2021, 12:49 PM
dschuff added inline comments.
llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
449

LGTM now

aheejin edited the summary of this revision. (Show Details)Aug 24 2021, 5:37 PM
This revision was automatically updated to reflect the committed changes.