diff --git a/ChangeLog.md b/ChangeLog.md index d5d6f6e7b89c2..a6bf8edea3bdb 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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. diff --git a/embuilder.py b/embuilder.py index ba3ae51f22d33..77a39dd8f0a81 100755 --- a/embuilder.py +++ b/embuilder.py @@ -32,6 +32,7 @@ MINIMAL_TASKS = [ 'libcompiler_rt', 'libcompiler_rt-legacysjlj', + 'libcompiler_rt-wasmsjlj', 'libcompiler_rt-ww', 'libc', 'libc-debug', @@ -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', @@ -80,6 +84,7 @@ 'crt1_proxy_main', 'crtbegin', 'libunwind-legacyexcept', + 'libunwind-wasmexcept', 'libnoexit', 'sqlite3', 'sqlite3-mt', diff --git a/site/source/docs/tools_reference/settings_reference.rst b/site/source/docs/tools_reference/settings_reference.rst index 1b58e77d0a362..ebdfa0d3593c5 100644 --- a/site/source/docs/tools_reference/settings_reference.rst +++ b/site/source/docs/tools_reference/settings_reference.rst @@ -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: diff --git a/src/settings.js b/src/settings.js index 21c9225fed810..db19c959bc2e7 100644 --- a/src/settings.js +++ b/src/settings.js @@ -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. diff --git a/test/other/codesize/test_codesize_cxx_except_wasm.size b/test/other/codesize/test_codesize_cxx_except_wasm.size index c3d6bbf133f26..667ffeb604020 100644 --- a/test/other/codesize/test_codesize_cxx_except_wasm.size +++ b/test/other/codesize/test_codesize_cxx_except_wasm.size @@ -1 +1 @@ -144652 +144531 diff --git a/test/test_other.py b/test/test_other.py index 98748707c99e6..8ceb8cf987f65 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -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', diff --git a/tools/building.py b/tools/building.py index 3d764fe82d33a..568335891c0c3 100644 --- a/tools/building.py +++ b/tools/building.py @@ -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'] @@ -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'] diff --git a/tools/link.py b/tools/link.py index ea6ce2bbd16c7..7639ad628fc59 100644 --- a/tools/link.py +++ b/tools/link.py @@ -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/ diff --git a/tools/settings.py b/tools/settings.py index d09504e97eb0a..133e9f1f4f2f2 100644 --- a/tools/settings.py +++ b/tools/settings.py @@ -84,6 +84,7 @@ 'INLINING_LIMIT', 'DISABLE_EXCEPTION_CATCHING', 'DISABLE_EXCEPTION_THROWING', + 'WASM_LEGACY_EXCEPTIONS', 'MAIN_MODULE', 'SIDE_MODULE', 'RELOCATABLE', diff --git a/tools/shared.py b/tools/shared.py index 3d60bd9d6ddd8..45b262820692d 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -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): diff --git a/tools/system_libs.py b/tools/system_libs.py index 3eed0447651df..c6e8ab6919031 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -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): @@ -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 @@ -805,6 +809,8 @@ 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 @@ -812,12 +818,16 @@ 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: @@ -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 @@ -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) @@ -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', @@ -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 @@ -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() @@ -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 diff --git a/tools/webassembly.py b/tools/webassembly.py index 3fff8ace24975..7bbc03b93fe78 100644 --- a/tools/webassembly.py +++ b/tools/webassembly.py @@ -75,6 +75,7 @@ class Type(IntEnum): V128 = 0x7b # -0x5 FUNCREF = 0x70 # -0x10 EXTERNREF = 0x6f # -0x11 + EXNREF = 0x69 # -0x17 VOID = 0x40 # -0x40