Skip to content

Commit

Permalink
[EH] Use LLVM implementation for new Wasm EH (#23469)
Browse files Browse the repository at this point in the history
This adds a new `WASM` mode in `ExceptionLibrary`, which uses the new
standard Wasm (exnref) EH, adds a library variant for it, and make the
tests use the new LLVM implementation instead of the Binaryen
translator.

The CI won't pass until llvm/llvm-project#123915
lands.

This requires adding `-wasmexcept` and `-wasmsjlj` variants to
`MINIMAL_TASKS` in `embuilder.py`
because they are necessary to run coreX tests in CircleCI, because
dylink coreX tests in CircleCI rellies on `./embuilder build MINIMAL_PIC
--pic`, which is the sum of `MINIMAL_TASKS` and `MINIMAL_PIC_TASKS`.

Because a new variant is added to `ExceptionLibrary`, this increases
`SYSTEM` library size from 366M to 400M, roughly by 11%. We discussed
about the possibility of not shipping the exnref libraries and let them
be built on demand, but given that `SYSTEM` include all variants, I'm
not sure how we are going to do that:
https://github.com/emscripten-core/emscripten/blob/73ebb91029948e62b3a4cea9ccc8db2dd87162b5/embuilder.py#L174-L177
  • Loading branch information
aheejin authored Jan 29, 2025
1 parent 5a327e9 commit 62304e2
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 18 deletions.
5 changes: 5 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ See docs/process.md for more on how version tagging works.

4.0.2 (in development)
----------------------
- The standard Wasm EH, enabled by `-sWASM_LEGACY_EXCEPTIONS=0`, now uses the
LLVM backend implementation rather than the previously used Binaryen
translator
(https://github.com/WebAssembly/binaryen/blob/main/src/passes/TranslateEH.cpp).
(#23469) No specific action from the user is required.
- Added support for compiling AVX2 intrinsics, 256-bit wide intrinsic is emulated
on top of 128-bit Wasm SIMD instruction set. (#23035). Pass `-msimd128 -mavx2`
to enable targeting AVX2.
Expand Down
5 changes: 5 additions & 0 deletions embuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
MINIMAL_TASKS = [
'libcompiler_rt',
'libcompiler_rt-legacysjlj',
'libcompiler_rt-wasmsjlj',
'libcompiler_rt-ww',
'libc',
'libc-debug',
Expand All @@ -40,13 +41,16 @@
'libc_optz-debug',
'libc++abi',
'libc++abi-legacyexcept',
'libc++abi-wasmexcept',
'libc++abi-noexcept',
'libc++abi-debug',
'libc++abi-debug-legacyexcept',
'libc++abi-debug-wasmexcept',
'libc++abi-debug-noexcept',
'libc++abi-debug-ww-noexcept',
'libc++',
'libc++-legacyexcept',
'libc++-wasmexcept',
'libc++-noexcept',
'libc++-ww-noexcept',
'libal',
Expand Down Expand Up @@ -80,6 +84,7 @@
'crt1_proxy_main',
'crtbegin',
'libunwind-legacyexcept',
'libunwind-wasmexcept',
'libnoexit',
'sqlite3',
'sqlite3-mt',
Expand Down
2 changes: 2 additions & 0 deletions site/source/docs/tools_reference/settings_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,8 @@ https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-
If false, emit instructions for the standardized exception handling proposal:
https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md

.. note:: Applicable during both linking and compilation

Default value: true

.. _nodejs_catch_exit:
Expand Down
2 changes: 1 addition & 1 deletion src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ var EXCEPTION_STACK_TRACES = false;
// https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md
// If false, emit instructions for the standardized exception handling proposal:
// https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md
// [link]
// [compile+link]
var WASM_LEGACY_EXCEPTIONS = true;

// Emscripten throws an ExitStatus exception to unwind when exit() is called.
Expand Down
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_cxx_except_wasm.size
Original file line number Diff line number Diff line change
@@ -1 +1 @@
144652
144531
3 changes: 3 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -3535,7 +3535,10 @@ def test_embind_tsgen_jspi(self):
'legacy': [1]
})
def test_embind_tsgen_exceptions(self, legacy):
if not legacy and shared.get_node_version(config.NODE_JS)[0] < 22:
self.skipTest('Node version needs to be 22 or greater to run tsgen with exnref')
self.set_setting('WASM_LEGACY_EXCEPTIONS', legacy)

# Check that when Wasm exceptions and assertions are enabled bindings still generate.
self.run_process([EMXX, test_file('other/embind_tsgen.cpp'),
'-lembind', '-fwasm-exceptions', '-sASSERTIONS',
Expand Down
10 changes: 10 additions & 0 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ def llvm_backend_args():
elif settings.SUPPORT_LONGJMP == 'wasm':
args += ['-wasm-enable-sjlj']

if settings.WASM_EXCEPTIONS:
if settings.WASM_LEGACY_EXCEPTIONS:
args += ['-wasm-use-legacy-eh']
else:
args += ['-wasm-use-legacy-eh=0']

# better (smaller, sometimes faster) codegen, see binaryen#1054
# and https://bugs.llvm.org/show_bug.cgi?id=39488
args += ['-disable-lsr']
Expand Down Expand Up @@ -277,6 +283,10 @@ def link_lld(args, target, external_symbols=None):

if settings.WASM_EXCEPTIONS:
cmd += ['-mllvm', '-wasm-enable-eh']
if settings.WASM_LEGACY_EXCEPTIONS:
cmd += ['-mllvm', '-wasm-use-legacy-eh']
else:
cmd += ['-mllvm', '-wasm-use-legacy-eh=0']
if settings.WASM_EXCEPTIONS or settings.SUPPORT_LONGJMP == 'wasm':
cmd += ['-mllvm', '-exception-model=wasm']

Expand Down
4 changes: 0 additions & 4 deletions tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,6 @@ def check_human_readable_list(items):
extras = settings.BINARYEN_EXTRA_PASSES.split(',')
passes += [('--' + p) if p[0] != '-' else p for p in extras if p]

# Run the translator to the standardized EH instructions.
if not settings.WASM_LEGACY_EXCEPTIONS:
passes += ['--emit-exnref']

# If we are going to run metadce then that means we will be running binaryen
# tools after the main invocation, whose flags are determined here
# (specifically we will run metadce and possibly also wasm-opt for import/
Expand Down
1 change: 1 addition & 0 deletions tools/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
'INLINING_LIMIT',
'DISABLE_EXCEPTION_CATCHING',
'DISABLE_EXCEPTION_THROWING',
'WASM_LEGACY_EXCEPTIONS',
'MAIN_MODULE',
'SIDE_MODULE',
'RELOCATABLE',
Expand Down
8 changes: 5 additions & 3 deletions tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,13 @@ def node_reference_types_flags(nodejs):

def node_exception_flags(nodejs):
node_version = get_node_version(nodejs)
# Exception handling was enabled by default in node v17.
# Legacy exception handling was enabled by default in node v17.
if node_version and node_version < (17, 0, 0):
return ['--experimental-wasm-eh']
else:
return []
# Standard exception handling was supported behind flag in node v22.
if node_version and node_version >= (22, 0, 0) and not settings.WASM_LEGACY_EXCEPTIONS:
return ['--experimental-wasm-exnref']
return []


def node_pthread_flags(nodejs):
Expand Down
40 changes: 31 additions & 9 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,10 +775,12 @@ class Exceptions(IntEnum):
- EMSCRIPTEN: Emscripten provides exception handling capability using JS
emulation. This causes code size increase and performance degradation.
- WASM_LEGACY: Wasm native exception handling support (legacy)
- WASM: Wasm native exception handling support
"""
NONE = auto()
EMSCRIPTEN = auto()
WASM_LEGACY = auto()
WASM = auto()


class ExceptionLibrary(Library):
Expand All @@ -793,7 +795,9 @@ def get_cflags(self):
elif self.eh_mode == Exceptions.EMSCRIPTEN:
cflags += ['-sDISABLE_EXCEPTION_CATCHING=0']
elif self.eh_mode == Exceptions.WASM_LEGACY:
cflags += ['-fwasm-exceptions']
cflags += ['-fwasm-exceptions', '-sWASM_LEGACY_EXCEPTIONS']
elif self.eh_mode == Exceptions.WASM:
cflags += ['-fwasm-exceptions', '-sWASM_LEGACY_EXCEPTIONS=0']

return cflags

Expand All @@ -805,19 +809,25 @@ def get_base_name(self):
name += '-noexcept'
elif self.eh_mode == Exceptions.WASM_LEGACY:
name += '-legacyexcept'
elif self.eh_mode == Exceptions.WASM:
name += '-wasmexcept'
return name

@classmethod
def variations(cls):
combos = super().variations()
return ([dict(eh_mode=Exceptions.NONE, **combo) for combo in combos] +
[dict(eh_mode=Exceptions.EMSCRIPTEN, **combo) for combo in combos] +
[dict(eh_mode=Exceptions.WASM_LEGACY, **combo) for combo in combos])
[dict(eh_mode=Exceptions.WASM_LEGACY, **combo) for combo in combos] +
[dict(eh_mode=Exceptions.WASM, **combo) for combo in combos])

@classmethod
def get_default_variation(cls, **kwargs):
if settings.WASM_EXCEPTIONS:
eh_mode = Exceptions.WASM_LEGACY
if settings.WASM_LEGACY_EXCEPTIONS:
eh_mode = Exceptions.WASM_LEGACY
else:
eh_mode = Exceptions.WASM
elif settings.DISABLE_EXCEPTION_CATCHING == 1:
eh_mode = Exceptions.NONE
else:
Expand All @@ -838,6 +848,12 @@ def get_cflags(self):
'-sDISABLE_EXCEPTION_THROWING=0']
elif self.eh_mode == Exceptions.WASM_LEGACY:
cflags += ['-sSUPPORT_LONGJMP=wasm',
'-sWASM_LEGACY_EXCEPTIONS',
'-sDISABLE_EXCEPTION_THROWING',
'-D__WASM_SJLJ__']
elif self.eh_mode == Exceptions.WASM:
cflags += ['-sSUPPORT_LONGJMP=wasm',
'-sWASM_LEGACY_EXCEPTIONS=0',
'-sDISABLE_EXCEPTION_THROWING',
'-D__WASM_SJLJ__']
return cflags
Expand All @@ -848,18 +864,24 @@ def get_base_name(self):
# suffixes. Change the default to wasm exception later.
if self.eh_mode == Exceptions.WASM_LEGACY:
name += '-legacysjlj'
elif self.eh_mode == Exceptions.WASM:
name += '-wasmsjlj'
return name

@classmethod
def variations(cls):
combos = super().variations()
return ([dict(eh_mode=Exceptions.EMSCRIPTEN, **combo) for combo in combos] +
[dict(eh_mode=Exceptions.WASM_LEGACY, **combo) for combo in combos])
[dict(eh_mode=Exceptions.WASM_LEGACY, **combo) for combo in combos] +
[dict(eh_mode=Exceptions.WASM, **combo) for combo in combos])

@classmethod
def get_default_variation(cls, **kwargs):
if settings.SUPPORT_LONGJMP == 'wasm':
eh_mode = Exceptions.WASM_LEGACY
if settings.WASM_LEGACY_EXCEPTIONS:
eh_mode = Exceptions.WASM_LEGACY
else:
eh_mode = Exceptions.WASM
else:
eh_mode = Exceptions.EMSCRIPTEN
return super().get_default_variation(eh_mode=eh_mode, **kwargs)
Expand Down Expand Up @@ -1600,7 +1622,7 @@ def get_files(self):
filenames += ['cxa_noexception.cpp']
elif self.eh_mode == Exceptions.EMSCRIPTEN:
filenames += ['cxa_exception_emscripten.cpp']
elif self.eh_mode == Exceptions.WASM_LEGACY:
elif self.eh_mode in (Exceptions.WASM_LEGACY, Exceptions.WASM):
filenames += [
'cxa_exception_storage.cpp',
'cxa_exception.cpp',
Expand Down Expand Up @@ -1654,7 +1676,7 @@ class libcxx(ExceptionLibrary, MTLibrary):

def get_cflags(self):
cflags = super().get_cflags()
if self.eh_mode == Exceptions.WASM_LEGACY:
if self.eh_mode in (Exceptions.WASM_LEGACY, Exceptions.WASM):
cflags.append('-D__WASM_EXCEPTIONS__')
return cflags

Expand All @@ -1677,7 +1699,7 @@ def __init__(self, **kwargs):
super().__init__(**kwargs)

def can_use(self):
return super().can_use() and self.eh_mode == Exceptions.WASM_LEGACY
return super().can_use() and self.eh_mode in (Exceptions.WASM_LEGACY, Exceptions.WASM)

def get_cflags(self):
cflags = super().get_cflags()
Expand All @@ -1688,7 +1710,7 @@ def get_cflags(self):
cflags.append('-D_LIBUNWIND_HAS_NO_EXCEPTIONS')
elif self.eh_mode == Exceptions.EMSCRIPTEN:
cflags.append('-D__EMSCRIPTEN_EXCEPTIONS__')
elif self.eh_mode == Exceptions.WASM_LEGACY:
elif self.eh_mode in (Exceptions.WASM_LEGACY, Exceptions.WASM):
cflags.append('-D__WASM_EXCEPTIONS__')
return cflags

Expand Down
1 change: 1 addition & 0 deletions tools/webassembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class Type(IntEnum):
V128 = 0x7b # -0x5
FUNCREF = 0x70 # -0x10
EXTERNREF = 0x6f # -0x11
EXNREF = 0x69 # -0x17
VOID = 0x40 # -0x40


Expand Down

0 comments on commit 62304e2

Please sign in to comment.