Skip to content

Commit f1700e4

Browse files
authored
Add wasm exception handling support (#10577)
- Adds a new interal setting `EXCEPTION_HANDLING` for the new wasm exception handling, and a new option `-fwasm-exceptions`. Setting `-fwasm-exceptions` sets `EXCEPTION_HANDLING` setting. - `NoExceptLibrary` in system_libs.py now support three modes: none, emscripten, and wasm. Libraries are built separately for each of these modes according to options. - Adds libcxxabi support. We share a lot of existing code with SjLj EH because our LSDA table structure is similar to that of SjLj EH, but there are some differenct parts. One important deviation of the wasm EH from other schemes is wasm EH does not have two-phase unwinding. Itanium-style two-phase unwinding typically consists of two phases: search and cleanup. Wasm EH only does one phase unwinding that does both search and cleanup together. - This patch replaces `throw()` with `_THROW` in libcxxabi, which uses `noexcept` keyword instead, because wasm EH's clang frontend does not yet support exception specifications, such as `throw()`. - Adds wasm libunwind implementation. Unlike other targets, we don't do the actual stack unwinding in libunwind. Instead, it contains target-specific method hooks that are used by libcxxabi. For example, `_Unwind_RaiseException` in libunwind is called by `__cxa_throw` in libcxxabi, whose call is generated when you use `throw` keyword in C++. - Adds `@with_both_exception_handling` decorator in test_core.py, which runs the same test on both emscripten and the new wasm EH. Currently many tests still fail on the wasm EH, so only passing tests are using this decorator and failing ones are marked as TODOs. The reason for most of the failures is the lack of support for exception specification (such as `throw()`), as noted above. Detailed info on toolchain convention of wasm EH handling is in https://github.com/WebAssembly/tool-conventions/blob/master/EHScheme.md.
1 parent 854c6e2 commit f1700e4

30 files changed

+10408
-107
lines changed

embuilder.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@
3737
'libcompiler_rt',
3838
'libc',
3939
'libc++abi',
40+
'libc++abi-except',
4041
'libc++abi-noexcept',
4142
'libc++',
43+
'libc++-except',
4244
'libc++-noexcept',
4345
'libal',
4446
'libdlmalloc',
@@ -51,6 +53,7 @@
5153
'libc-wasm',
5254
'libstandalonewasm',
5355
'crt1',
56+
'libunwind-except'
5457
]
5558

5659
USER_TASKS = [

emcc.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,7 +1432,7 @@ def is_supported_link_flag(f):
14321432

14331433
# if exception catching is disabled, we can prevent that code from being
14341434
# generated in the frontend
1435-
if shared.Settings.DISABLE_EXCEPTION_CATCHING == 1 and shared.Settings.WASM_BACKEND:
1435+
if shared.Settings.DISABLE_EXCEPTION_CATCHING == 1 and shared.Settings.WASM_BACKEND and not shared.Settings.EXCEPTION_HANDLING:
14361436
newargs.append('-fignore-exceptions')
14371437

14381438
if shared.Settings.DEAD_FUNCTIONS:
@@ -2729,6 +2729,8 @@ def parse_args(newargs):
27292729
options = EmccOptions()
27302730
settings_changes = []
27312731
should_exit = False
2732+
eh_enabled = False
2733+
wasm_eh_enabled = False
27322734

27332735
def check_bad_eq(arg):
27342736
if '=' in arg:
@@ -2980,13 +2982,15 @@ def check_bad_eq(arg):
29802982
settings_changes.append('PTHREADS_PROFILING=1')
29812983
newargs[i] = ''
29822984
elif newargs[i] == '-fno-exceptions':
2983-
settings_changes.append('DISABLE_EXCEPTION_CATCHING=1')
2984-
settings_changes.append('DISABLE_EXCEPTION_THROWING=1')
2985+
shared.Settings.DISABLE_EXCEPTION_CATCHING = 1
2986+
shared.Settings.DISABLE_EXCEPTION_THROWING = 1
2987+
shared.Settings.EXCEPTION_HANDLING = 0
29852988
elif newargs[i] == '-fexceptions':
2986-
settings_changes.append('DISABLE_EXCEPTION_CATCHING=0')
2987-
settings_changes.append('DISABLE_EXCEPTION_THROWING=0')
2989+
eh_enabled = True
2990+
elif newargs[i] == '-fwasm-exceptions':
2991+
wasm_eh_enabled = True
29882992
elif newargs[i] == '-fignore-exceptions':
2989-
settings_changes.append('DISABLE_EXCEPTION_CATCHING=1')
2993+
shared.Settings.DISABLE_EXCEPTION_CATCHING = 1
29902994
elif newargs[i] == '--default-obj-ext':
29912995
newargs[i] = ''
29922996
options.default_object_extension = newargs[i + 1]
@@ -3028,6 +3032,18 @@ def check_bad_eq(arg):
30283032
elif newargs[i] == '-fno-rtti':
30293033
shared.Settings.USE_RTTI = 0
30303034

3035+
# TODO Currently -fexceptions only means Emscripten EH. Switch to wasm
3036+
# exception handling by default when -fexceptions is given when wasm
3037+
# exception handling becomes stable.
3038+
if wasm_eh_enabled:
3039+
shared.Settings.EXCEPTION_HANDLING = 1
3040+
shared.Settings.DISABLE_EXCEPTION_THROWING = 1
3041+
shared.Settings.DISABLE_EXCEPTION_CATCHING = 1
3042+
elif eh_enabled:
3043+
shared.Settings.EXCEPTION_HANDLING = 0
3044+
shared.Settings.DISABLE_EXCEPTION_THROWING = 0
3045+
shared.Settings.DISABLE_EXCEPTION_CATCHING = 0
3046+
30313047
if should_exit:
30323048
sys.exit(0)
30333049

src/settings.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ var ENVIRONMENT = '';
589589
// * LZ4 files are read-only.
590590
var LZ4 = 0;
591591

592+
// Emscripten exception handling options.
593+
// These options only pertain to Emscripten exception handling and do not
594+
// control the experimental native wasm exception handling option.
595+
592596
// Disables generating code to actually catch exceptions. This disabling is on
593597
// by default as the overhead of exceptions is quite high in size and speed
594598
// currently (in the future, wasm should improve that). When exceptions are

src/settings_internal.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,6 @@ var LTO = 0;
180180
// sections.
181181
// This has no effect if DWARF is not being emitted.
182182
var SEPARATE_DWARF = 0;
183+
184+
// New WebAssembly exception handling (experimental)
185+
var EXCEPTION_HANDLING = 0;

system/lib/libcxxabi/include/cxxabi.h

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ class type_info; // forward declaration
3232
#endif
3333
}
3434

35-
// XXX EMSCRIPTEN Wasm exception handling has not yet implemented support for
35+
// XXX EMSCRIPTEN: Wasm exception handling has not yet implemented support for
3636
// exception specification. This temporarily changes 'throw()` with 'noexcept'
3737
// to make wasm EH working in the interim.
3838
#ifdef __USING_WASM_EXCEPTIONS__
39-
#define NOTHROW noexcept
39+
#define _NOTHROW noexcept
4040
#else
41-
#define NOTHROW throw()
41+
#define _NOTHROW throw()
4242
#endif
4343

4444
// runtime routines use C calling conventions, but are in __cxxabiv1 namespace
@@ -47,20 +47,24 @@ extern "C" {
4747

4848
// 2.4.2 Allocating the Exception Object
4949
extern _LIBCXXABI_FUNC_VIS void *
50-
__cxa_allocate_exception(size_t thrown_size) NOTHROW;
50+
__cxa_allocate_exception(size_t thrown_size) _NOTHROW;
5151
extern _LIBCXXABI_FUNC_VIS void
52-
__cxa_free_exception(void *thrown_exception) NOTHROW;
52+
__cxa_free_exception(void *thrown_exception) _NOTHROW;
5353

5454
// 2.4.3 Throwing the Exception Object
5555
extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
5656
__cxa_throw(void *thrown_exception, std::type_info *tinfo,
57+
#ifdef __USING_WASM_EXCEPTIONS__
58+
void *(*dest)(void *));
59+
#else
5760
void (*dest)(void *));
61+
#endif
5862

5963
// 2.5.3 Exception Handlers
6064
extern _LIBCXXABI_FUNC_VIS void *
61-
__cxa_get_exception_ptr(void *exceptionObject) NOTHROW;
65+
__cxa_get_exception_ptr(void *exceptionObject) _NOTHROW;
6266
extern _LIBCXXABI_FUNC_VIS void *
63-
__cxa_begin_catch(void *exceptionObject) NOTHROW;
67+
__cxa_begin_catch(void *exceptionObject) _NOTHROW;
6468
extern _LIBCXXABI_FUNC_VIS void __cxa_end_catch();
6569
#if defined(_LIBCXXABI_ARM_EHABI)
6670
extern _LIBCXXABI_FUNC_VIS bool
@@ -155,17 +159,17 @@ extern _LIBCXXABI_FUNC_VIS char *__cxa_demangle(const char *mangled_name,
155159

156160
// Apple additions to support C++ 0x exception_ptr class
157161
// These are primitives to wrap a smart pointer around an exception object
158-
extern _LIBCXXABI_FUNC_VIS void *__cxa_current_primary_exception() NOTHROW;
162+
extern _LIBCXXABI_FUNC_VIS void *__cxa_current_primary_exception() _NOTHROW;
159163
extern _LIBCXXABI_FUNC_VIS void
160164
__cxa_rethrow_primary_exception(void *primary_exception);
161165
extern _LIBCXXABI_FUNC_VIS void
162-
__cxa_increment_exception_refcount(void *primary_exception) NOTHROW;
166+
__cxa_increment_exception_refcount(void *primary_exception) _NOTHROW;
163167
extern _LIBCXXABI_FUNC_VIS void
164-
__cxa_decrement_exception_refcount(void *primary_exception) NOTHROW;
168+
__cxa_decrement_exception_refcount(void *primary_exception) _NOTHROW;
165169

166170
// Apple extension to support std::uncaught_exception()
167-
extern _LIBCXXABI_FUNC_VIS bool __cxa_uncaught_exception() NOTHROW;
168-
extern _LIBCXXABI_FUNC_VIS unsigned int __cxa_uncaught_exceptions() NOTHROW;
171+
extern _LIBCXXABI_FUNC_VIS bool __cxa_uncaught_exception() _NOTHROW;
172+
extern _LIBCXXABI_FUNC_VIS unsigned int __cxa_uncaught_exceptions() _NOTHROW;
169173

170174
#if defined(__linux__) || defined(__Fuchsia__)
171175
// Linux and Fuchsia TLS support. Not yet an official part of the Itanium ABI.

system/lib/libcxxabi/readme.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ Local modifications are marked with the comment: 'XXX EMSCRIPTEN'
1616
1. Add __cxa_can_catch and __cxa_is_pointer_type to private_typeinfo.cpp.
1717

1818
2. Duplicate __isOurExceptionClass in cxa_handlers.cpp since we don't compile
19-
cxa_exception.cpp.
19+
cxa_exception.cpp in Emscripten EH mode.
2020

21-
3. Define and use NOTHROW macro in cxxabi.h
21+
3. Define and use _NOTHROW macro in cxxabi.h

system/lib/libcxxabi/src/cxa_exception.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ extern "C" {
181181
// object. Zero-fill the object. If memory can't be allocated, call
182182
// std::terminate. Return a pointer to the memory to be used for the
183183
// user's exception object.
184-
void *__cxa_allocate_exception(size_t thrown_size) throw() {
184+
void *__cxa_allocate_exception(size_t thrown_size) _NOTHROW {
185185
size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size);
186186

187187
// Allocate extra space before the __cxa_exception header to ensure the
@@ -199,7 +199,7 @@ void *__cxa_allocate_exception(size_t thrown_size) throw() {
199199

200200

201201
// Free a __cxa_exception object allocated with __cxa_allocate_exception.
202-
void __cxa_free_exception(void *thrown_object) throw() {
202+
void __cxa_free_exception(void *thrown_object) _NOTHROW {
203203
// Compute the size of the padding before the header.
204204
size_t header_offset = get_cxa_exception_offset();
205205
char *raw_buffer =
@@ -255,7 +255,12 @@ will call terminate, assuming that there was no handler for the
255255
exception.
256256
*/
257257
void
258+
#ifdef __USING_WASM_EXCEPTIONS__
259+
// In wasm, destructors return their argument
260+
__cxa_throw(void *thrown_object, std::type_info *tinfo, void *(*dest)(void *)) {
261+
#else
258262
__cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *)) {
263+
#endif
259264
__cxa_eh_globals *globals = __cxa_get_globals();
260265
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
261266

@@ -293,7 +298,7 @@ The adjusted pointer is computed by the personality routine during phase 1
293298
294299
Requires: exception is native
295300
*/
296-
void *__cxa_get_exception_ptr(void *unwind_exception) throw() {
301+
void *__cxa_get_exception_ptr(void *unwind_exception) _NOTHROW {
297302
#if defined(_LIBCXXABI_ARM_EHABI)
298303
return reinterpret_cast<void*>(
299304
static_cast<_Unwind_Control_Block*>(unwind_exception)->barrier_cache.bitpattern[0]);
@@ -308,7 +313,7 @@ void *__cxa_get_exception_ptr(void *unwind_exception) throw() {
308313
The routine to be called before the cleanup. This will save __cxa_exception in
309314
__cxa_eh_globals, so that __cxa_end_cleanup() can recover later.
310315
*/
311-
bool __cxa_begin_cleanup(void *unwind_arg) throw() {
316+
bool __cxa_begin_cleanup(void *unwind_arg) _NOTHROW {
312317
_Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg);
313318
__cxa_eh_globals* globals = __cxa_get_globals();
314319
__cxa_exception* exception_header =
@@ -419,7 +424,7 @@ to terminate or unexpected during unwinding.
419424
_Unwind_Exception and return a pointer to that.
420425
*/
421426
void*
422-
__cxa_begin_catch(void* unwind_arg) throw()
427+
__cxa_begin_catch(void* unwind_arg) _NOTHROW
423428
{
424429
_Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg);
425430
bool native_exception = __isOurExceptionClass(unwind_exception);
@@ -627,7 +632,7 @@ void __cxa_rethrow() {
627632
Requires: If thrown_object is not NULL, it is a native exception.
628633
*/
629634
void
630-
__cxa_increment_exception_refcount(void *thrown_object) throw() {
635+
__cxa_increment_exception_refcount(void *thrown_object) _NOTHROW {
631636
if (thrown_object != NULL )
632637
{
633638
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
@@ -644,7 +649,7 @@ __cxa_increment_exception_refcount(void *thrown_object) throw() {
644649
Requires: If thrown_object is not NULL, it is a native exception.
645650
*/
646651
_LIBCXXABI_NO_CFI
647-
void __cxa_decrement_exception_refcount(void *thrown_object) throw() {
652+
void __cxa_decrement_exception_refcount(void *thrown_object) _NOTHROW {
648653
if (thrown_object != NULL )
649654
{
650655
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
@@ -667,7 +672,7 @@ void __cxa_decrement_exception_refcount(void *thrown_object) throw() {
667672
been no exceptions thrown, ever, on this thread, we can return NULL without
668673
the need to allocate the exception-handling globals.
669674
*/
670-
void *__cxa_current_primary_exception() throw() {
675+
void *__cxa_current_primary_exception() _NOTHROW {
671676
// get the current exception
672677
__cxa_eh_globals* globals = __cxa_get_globals_fast();
673678
if (NULL == globals)
@@ -739,10 +744,10 @@ __cxa_rethrow_primary_exception(void* thrown_object)
739744
}
740745

741746
bool
742-
__cxa_uncaught_exception() throw() { return __cxa_uncaught_exceptions() != 0; }
747+
__cxa_uncaught_exception() _NOTHROW { return __cxa_uncaught_exceptions() != 0; }
743748

744749
unsigned int
745-
__cxa_uncaught_exceptions() throw()
750+
__cxa_uncaught_exceptions() _NOTHROW
746751
{
747752
// This does not report foreign exceptions in flight
748753
__cxa_eh_globals* globals = __cxa_get_globals_fast();

system/lib/libcxxabi/src/cxa_exception.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ struct _LIBCXXABI_HIDDEN __cxa_exception {
3838

3939
// Manage the exception object itself.
4040
std::type_info *exceptionType;
41+
#ifdef __USING_WASM_EXCEPTIONS__
42+
// In wasm, destructors return their argument
43+
void *(*exceptionDestructor)(void *);
44+
#else
4145
void (*exceptionDestructor)(void *);
46+
#endif
4247
std::unexpected_handler unexpectedHandler;
4348
std::terminate_handler terminateHandler;
4449

system/lib/libcxxabi/src/cxa_handlers.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121

2222
namespace __cxxabiv1 {
2323

24-
// XXX EMSCRIPTEN: Copied from cxa_exception.cpp since we don't compile that file.
25-
// Note that in no-exceptions builds we include cxa_noexception
26-
// which provides stubs of those anyhow.
27-
#if defined(__EMSCRIPTEN__) && !defined(_LIBCXXABI_NO_EXCEPTIONS)
24+
#ifdef __USING_EMSCRIPTEN_EXCEPTIONS__
25+
// XXX EMSCRIPTEN: Copied from cxa_exception.cpp since we don't compile that
26+
// file in Emscripten EH mode. Note that in no-exceptions builds we include
27+
// cxa_noexception.cpp which provides stubs of those anyhow.
28+
2829
// Is it one of ours?
2930
uint64_t __getExceptionClass(const _Unwind_Exception* unwind_exception) {
3031
// On x86 and some ARM unwinders, unwind_exception->exception_class is

0 commit comments

Comments
 (0)