From fbe49e173912ddf3b53a5def2d59dbadf8d55967 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Wed, 23 Feb 2022 10:27:09 -0800 Subject: [PATCH 1/5] Move parts of emscripten exception handling to native code. NFC Specifically, this change moves the allocation and reference counting functions. This saves on code size but more importantly reduces the number and of complexity of imports/exports, which in turn helps with the wasm64 work I've been doing. --- emcc.py | 7 + src/jsifier.js | 8 +- src/library_exceptions.js | 98 +------- src/library_exceptions_stub.js | 4 - .../src/cxa_exception_emscripten.cpp | 216 +++++++++--------- .../libcxxabi/src/cxa_exception_js_utils.cpp | 105 +++++++++ system/lib/libcxxabi/src/cxa_noexception.cpp | 11 + system/lib/standalone/standalone.c | 5 - test/core/test_exceptions_primary.cpp | 3 +- test/no_this_in_dyncall.out | 2 +- .../metadce/test_metadce_cxx_except.exports | 2 + .../metadce/test_metadce_cxx_except.jssize | 2 +- .../metadce/test_metadce_cxx_except.size | 2 +- .../metadce/test_metadce_cxx_mangle.exports | 5 + .../metadce/test_metadce_cxx_noexcept.exports | 1 + .../metadce/test_metadce_cxx_noexcept.jssize | 2 +- .../metadce/test_metadce_cxx_noexcept.size | 2 +- .../metadce/test_metadce_minimal_O0.jssize | 2 +- test/test_core.py | 6 +- tools/deps_info.py | 22 +- tools/system_libs.py | 6 +- 21 files changed, 280 insertions(+), 231 deletions(-) create mode 100644 system/lib/libcxxabi/src/cxa_exception_js_utils.cpp diff --git a/emcc.py b/emcc.py index a1ad29b7228cd..32bdda680fe01 100755 --- a/emcc.py +++ b/emcc.py @@ -2785,6 +2785,13 @@ def check_memory_setting(setting): '__cxa_is_pointer_type', '__cxa_can_catch', + # __cxa_begin_catch depends on this but we can't use deps info in this + # case because that only works for user-level code, and __cxa_begin_catch + # can be used by the standard library. + '__cxa_increment_exception_refcount', + # Same for __cxa_end_catch + '__cxa_decrement_exception_refcount', + # Emscripten exception handling can generate invoke calls, and they call # setThrew(). We cannot handle this using deps_info as the invokes are not # emitted because of library function usage, but by codegen itself. diff --git a/src/jsifier.js b/src/jsifier.js index a966e7083cc05..273203082723d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -308,13 +308,13 @@ function ${name}(${args}) { warn('To build in STANDALONE_WASM mode without a main(), use emcc --no-entry'); } } + // We have already warned/errored about this function, so for the purposes of Closure use, mute all type checks + // regarding this function, marking ot a variadic function that can take in anything and return anything. + // (not useful to warn/error multiple times) + LibraryManager.library[symbol + '__docs'] = '/** @type {function(...*):?} */'; if (!RELOCATABLE) { // emit a stub that will fail at runtime LibraryManager.library[symbol] = new Function(`err('missing function: ${symbol}'); abort(-1);`); - // We have already warned/errored about this function, so for the purposes of Closure use, mute all type checks - // regarding this function, marking ot a variadic function that can take in anything and return anything. - // (not useful to warn/error multiple times) - LibraryManager.library[symbol + '__docs'] = '/** @type {function(...*):?} */'; } else { // Create a stub for this symbol which can later be replaced by the // dynamic linker. If this stub is called before the symbol is diff --git a/src/library_exceptions.js b/src/library_exceptions.js index bb3281330a3ea..d64e217cf1aa5 100644 --- a/src/library_exceptions.js +++ b/src/library_exceptions.js @@ -23,11 +23,9 @@ var LibraryExceptions = { // // excPtr - Thrown object pointer to wrap. Metadata pointer is calculated from it. $ExceptionInfo__docs: '/** @constructor */', - $ExceptionInfo__deps: ['__cxa_is_pointer_type', #if EXCEPTION_DEBUG - '$ptrToString', + $ExceptionInfo__deps: ['$ptrToString'], #endif - ], $ExceptionInfo: function(excPtr) { this.excPtr = excPtr; this.ptr = excPtr - {{{ C_STRUCTS.__cxa_exception.__size__ }}}; @@ -48,10 +46,6 @@ var LibraryExceptions = { return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionDestructor, '*') }}}; }; - this.set_refcount = function(refcount) { - {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.referenceCount, 'refcount', 'i32') }}}; - }; - this.set_caught = function (caught) { caught = caught ? 1 : 0; {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.caught, 'caught', 'i8') }}}; @@ -78,34 +72,8 @@ var LibraryExceptions = { this.set_adjusted_ptr(0); this.set_type(type); this.set_destructor(destructor); - this.set_refcount(0); - this.set_caught(false); - this.set_rethrown(false); } - this.add_ref = function() { -#if SHARED_MEMORY - Atomics.add(HEAP32, (this.ptr + {{{ C_STRUCTS.__cxa_exception.referenceCount }}}) >> 2, 1); -#else - var value = {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.referenceCount, 'i32') }}}; - {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.referenceCount, 'value + 1', 'i32') }}}; -#endif - }; - - // Returns true if last reference released. - this.release_ref = function() { -#if SHARED_MEMORY - var prev = Atomics.sub(HEAP32, (this.ptr + {{{ C_STRUCTS.__cxa_exception.referenceCount }}}) >> 2, 1); -#else - var prev = {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.referenceCount, 'i32') }}}; - {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.referenceCount, 'prev - 1', 'i32') }}}; -#endif -#if ASSERTIONS - assert(prev > 0); -#endif - return prev === 1; - }; - this.set_adjusted_ptr = function(adjustedPtr) { {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.adjustedPtr, 'adjustedPtr', '*') }}}; }; @@ -131,52 +99,6 @@ var LibraryExceptions = { }; }, - $exception_addRef: function (info) { -#if EXCEPTION_DEBUG - dbg('exception_addRef ' + ptrToString(info.excPtr)); -#endif - info.add_ref(); - }, - - $exception_decRef__deps: ['__cxa_free_exception' -#if EXCEPTION_DEBUG - , '$exceptionLast', '$exceptionCaught' -#endif - ], - $exception_decRef: function(info) { -#if EXCEPTION_DEBUG - dbg('exception_decRef ' + ptrToString(info.excPtr)); -#endif - // A rethrown exception can reach refcount 0; it must not be discarded - // Its next handler will clear the rethrown flag and addRef it, prior to - // final decRef and destruction here - if (info.release_ref() && !info.get_rethrown()) { - var destructor = info.get_destructor(); - if (destructor) { - // In Wasm, destructors return 'this' as in ARM - {{{ makeDynCall('pp', 'destructor') }}}(info.excPtr); - } - ___cxa_free_exception(info.excPtr); -#if EXCEPTION_DEBUG - dbg('decref freeing exception ' + [ptrToString(info.excPtr), exceptionLast, 'stack', exceptionCaught]); -#endif - } - }, - - __cxa_increment_exception_refcount__deps: ['$exception_addRef', '$ExceptionInfo'], - __cxa_increment_exception_refcount__sig: 'vp', - __cxa_increment_exception_refcount: function(ptr) { - if (!ptr) return; - exception_addRef(new ExceptionInfo(ptr)); - }, - - __cxa_decrement_exception_refcount__deps: ['$exception_decRef', '$ExceptionInfo'], - __cxa_decrement_exception_refcount__sig: 'vp', - __cxa_decrement_exception_refcount: function(ptr) { - if (!ptr) return; - exception_decRef(new ExceptionInfo(ptr)); - }, - // Here, we throw an exception after recording a couple of values that we need to remember // We also remember that it was the last exception thrown as we need to know that later. __cxa_throw__sig: 'vppp', @@ -224,7 +146,8 @@ var LibraryExceptions = { return type; }, - __cxa_begin_catch__deps: ['$exceptionCaught', '$exception_addRef', '$uncaughtExceptionCount'], + __cxa_begin_catch__deps: ['$exceptionCaught', '__cxa_increment_exception_refcount', + '$uncaughtExceptionCount'], __cxa_begin_catch__sig: 'pp', __cxa_begin_catch: function(ptr) { var info = new ExceptionInfo(ptr); @@ -237,7 +160,7 @@ var LibraryExceptions = { #if EXCEPTION_DEBUG dbg('__cxa_begin_catch ' + [ptrToString(ptr), 'stack', exceptionCaught]); #endif - exception_addRef(info); + ___cxa_increment_exception_refcount(info.excPtr); return info.get_exception_ptr(); }, @@ -245,7 +168,7 @@ var LibraryExceptions = { // and free the exception. Note that if the dynCall on the destructor fails // due to calling apply on undefined, that means that the destructor is // an invalid index into the FUNCTION_TABLE, so something has gone wrong. - __cxa_end_catch__deps: ['$exceptionCaught', '$exceptionLast', '$exception_decRef'], + __cxa_end_catch__deps: ['$exceptionCaught', '$exceptionLast', '__cxa_decrement_exception_refcount', 'setThrew'], __cxa_end_catch__sig: 'v', __cxa_end_catch: function() { // Clear state flag. @@ -259,17 +182,18 @@ var LibraryExceptions = { #if EXCEPTION_DEBUG dbg('__cxa_end_catch popped ' + [info, exceptionLast, 'stack', exceptionCaught]); #endif - exception_decRef(info); + ___cxa_decrement_exception_refcount(info.excPtr); exceptionLast = 0; // XXX in decRef? }, __cxa_get_exception_ptr__deps: ['$ExceptionInfo'], __cxa_get_exception_ptr__sig: 'pp', __cxa_get_exception_ptr: function(ptr) { + var rtn = new ExceptionInfo(ptr).get_exception_ptr(); #if EXCEPTION_DEBUG - dbg('__cxa_get_exception_ptr ' + ptrToString(ptr)); + err('__cxa_get_exception_ptr ' + ptrToString(ptr) + ' -> ' + ptrToString(rtn)); #endif - return new ExceptionInfo(ptr).get_exception_ptr(); + return rtn; }, __cxa_uncaught_exceptions__deps: ['$uncaughtExceptionCount'], @@ -285,13 +209,13 @@ var LibraryExceptions = { throw exception; }, - __cxa_current_primary_exception__deps: ['$exceptionCaught', '$exception_addRef'], + __cxa_current_primary_exception__deps: ['$exceptionCaught', '__cxa_increment_exception_refcount'], __cxa_current_primary_exception: function() { if (!exceptionCaught.length) { return 0; } var info = exceptionCaught[exceptionCaught.length - 1]; - exception_addRef(info); + ___cxa_increment_exception_refcount(info.excPtr); return info.excPtr; }, diff --git a/src/library_exceptions_stub.js b/src/library_exceptions_stub.js index e0a10712498db..16c7e532644c4 100644 --- a/src/library_exceptions_stub.js +++ b/src/library_exceptions_stub.js @@ -7,10 +7,6 @@ var LibraryExceptions = {}; [ - '__cxa_allocate_exception', - '__cxa_free_exception', - '__cxa_increment_exception_refcount', - '__cxa_decrement_exception_refcount', '__cxa_throw', '__cxa_rethrow', 'llvm_eh_typeid_for', diff --git a/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp b/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp index 0e408898c32fc..8f74073678e88 100644 --- a/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp +++ b/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp @@ -1,21 +1,30 @@ -//===------------------- cxa_exception_emscripten.cpp ---------------------===// +//===------------------------- cxa_emscripten.cpp -------------------------===// // -// This code contains Emscripten specific code for exception handling. -// Emscripten has two modes of exception handling: Emscripten EH, which uses JS -// glue code, and Wasm EH, which uses the new Wasm exception handling proposal -// and meant to be faster. Code for different modes is demarcated with -// '__USING_EMSCRIPTEN_EXCEPTIONS__' and '__USING_WASM_EXCEPTIONS__'. +// Most code in the file is directly copied from cxa_exception.cpp. +// TODO(sbc): consider merging them +// +// Notable changes: + // __cxa_allocate_exception doesn't add get_cxa_exception_offset +// __cxa_decrement_exception_refcount dosn't call the destructor if rethrown +// Both of these changes are mirrored from the historical JS implemenation of +// thse functions. // //===----------------------------------------------------------------------===// +#include "cxxabi.h" #include "cxa_exception.h" -#include "private_typeinfo.h" -#include -#include -#include -#include +#include "include/atomic_support.h" +#include "fallback_malloc.h" +#include "stdio.h" +#include "assert.h" + +// Define to enable extra debugging on stderr. +#if EXCEPTIONS_DEBUG +#define DEBUG printf +#else +#define DEBUG(...) +#endif -#if !defined(__USING_WASM_EXCEPTIONS__) // Until recently, Rust's `rust_eh_personality` for emscripten referred to this // symbol. If Emscripten doesn't provide it, there will be errors when linking // rust. The rust personality function is never called so we can just abort. @@ -30,121 +39,116 @@ __gxx_personality_v0(int version, _Unwind_Context* context) { abort(); } -#endif // !defined(__USING_WASM_EXCEPTIONS__) -using namespace __cxxabiv1; +namespace __cxxabiv1 { -// Some utility routines are copied from cxa_exception.cpp -static inline __cxa_exception* -cxa_exception_from_thrown_object(void* thrown_object) { - return static_cast<__cxa_exception*>(thrown_object) - 1; +// Utility routines +static +inline +__cxa_exception* +cxa_exception_from_thrown_object(void* thrown_object) +{ + DEBUG("cxa_exception_from_thrown_object %p -> %p\n", + thrown_object, static_cast<__cxa_exception*>(thrown_object) - 1); + return static_cast<__cxa_exception*>(thrown_object) - 1; } // Note: This is never called when exception_header is masquerading as a // __cxa_dependent_exception. -static inline void* -thrown_object_from_cxa_exception(__cxa_exception* exception_header) { - return static_cast(exception_header + 1); +static +inline +void* +thrown_object_from_cxa_exception(__cxa_exception* exception_header) +{ + DEBUG("thrown_object_from_cxa_exception %p -> %p\n", + exception_header, static_cast(exception_header + 1)); + return static_cast(exception_header + 1); } -#if defined(__USING_EMSCRIPTEN_EXCEPTIONS__) || \ - defined(__USING_WASM_EXCEPTIONS__) - -// Get the exception object from the unwind pointer. -// Relies on the structure layout, where the unwind pointer is right in -// front of the user's exception object -static inline __cxa_exception* cxa_exception_from_unwind_exception( - _Unwind_Exception* unwind_exception) { - return cxa_exception_from_thrown_object(unwind_exception + 1); +// Round s up to next multiple of a. +static inline +size_t aligned_allocation_size(size_t s, size_t a) { + return (s + a - 1) & ~(a - 1); } -static inline void* thrown_object_from_unwind_exception( - _Unwind_Exception* unwind_exception) { - __cxa_exception* exception_header = - cxa_exception_from_unwind_exception(unwind_exception); - return thrown_object_from_cxa_exception(exception_header); +static inline +size_t cxa_exception_size_from_exception_thrown_size(size_t size) { + return aligned_allocation_size(size + sizeof (__cxa_exception), + alignof(__cxa_exception)); } extern "C" { -void* __thrown_object_from_unwind_exception( - _Unwind_Exception* unwind_exception) { - return thrown_object_from_unwind_exception(unwind_exception); +// Allocate a __cxa_exception object, and zero-fill it. +// Reserve "thrown_size" bytes on the end for the user's exception +// object. Zero-fill the object. If memory can't be allocated, call +// std::terminate. Return a pointer to the memory to be used for the +// user's exception object. +void *__cxa_allocate_exception(size_t thrown_size) _NOEXCEPT { + size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size); + + char *raw_buffer = + (char *)__aligned_malloc_with_fallback(actual_size); + if (NULL == raw_buffer) + std::terminate(); + __cxa_exception *exception_header = + static_cast<__cxa_exception *>((void *)(raw_buffer)); + ::memset(exception_header, 0, actual_size); + return thrown_object_from_cxa_exception(exception_header); } -// Given a thrown_object, puts the information about its type and message into -// 'type' and 'message' output parameters. 'type' will contain the string -// representation of the type of the exception, e.g., 'int'. 'message' will -// contain the result of 'std::exception::what()' method if the type of the -// exception is a subclass of std::exception; otherwise it will be NULL. The -// caller is responsible for freeing 'type' buffer and also 'message' buffer, if -// it is not NULL. -void __get_exception_message(void* thrown_object, char** type, char** message) { - __cxa_exception* exception_header = - cxa_exception_from_thrown_object(thrown_object); - const __shim_type_info* thrown_type = - static_cast(exception_header->exceptionType); - const char* type_name = thrown_type->name(); - - int status = 0; - char* demangled_buf = __cxa_demangle(type_name, 0, 0, &status); - if (status == 0 && demangled_buf) { - *type = demangled_buf; - } else { - if (demangled_buf) { - free(demangled_buf); - } - *type = (char*)malloc(strlen(type_name) + 1); - strcpy(*type, type_name); - } - - *message = NULL; - const __shim_type_info* catch_type = - static_cast(&typeid(std::exception)); - int can_catch = catch_type->can_catch(thrown_type, thrown_object); - if (can_catch) { - const char* what = - static_cast(thrown_object)->what(); - *message = (char*)malloc(strlen(what) + 1); - strcpy(*message, what); - } -} -// Returns a message saying that execution was terminated due to an exception. -// This message is freshly malloc'd and should be freed. -char* __get_exception_terminate_message(void* thrown_object) { - char* type; - char* message; - __get_exception_message(thrown_object, &type, &message); - char* result; - if (message != NULL) { - asprintf( - &result, "terminating with uncaught exception %s: %s", type, message); - free(message); - } else { - asprintf(&result, "terminating with uncaught exception of type %s", type); - } - free(type); - return result; -} +// Free a __cxa_exception object allocated with __cxa_allocate_exception. +void __cxa_free_exception(void *thrown_object) _NOEXCEPT { + // Compute the size of the padding before the header. + char *raw_buffer = + ((char *)cxa_exception_from_thrown_object(thrown_object)); + __aligned_free_with_fallback((void *)raw_buffer); } -#endif // __USING_EMSCRIPTEN_EXCEPTIONS__ || __USING_WASM_EXCEPTIONS__ - -#ifndef __USING_WASM_EXCEPTIONS__ - -namespace __cxxabiv1 { - -void* __cxa_allocate_exception(size_t size) _NOEXCEPT { - // Thrown object is prepended by exception metadata block - __cxa_exception* ex = (__cxa_exception*)malloc(size + sizeof(__cxa_exception)); - return thrown_object_from_cxa_exception(ex); +/* + If thrown_object is not null, atomically increment the referenceCount field + of the __cxa_exception header associated with the thrown object referred to + by thrown_object. + + Requires: If thrown_object is not NULL, it is a native exception. +*/ +void +__cxa_increment_exception_refcount(void *thrown_object) _NOEXCEPT { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + DEBUG("INC: %p refcnt=%zu\n", thrown_object, exception_header->referenceCount); + std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1)); + } } -void __cxa_free_exception(void *thrown_object) _NOEXCEPT { - free(cxa_exception_from_thrown_object(thrown_object)); +/* + If thrown_object is not null, atomically decrement the referenceCount field + of the __cxa_exception header associated with the thrown object referred to + by thrown_object. If the referenceCount drops to zero, destroy and + deallocate the exception. + + Requires: If thrown_object is not NULL, it is a native exception. +*/ +_LIBCXXABI_NO_CFI +void __cxa_decrement_exception_refcount(void *thrown_object) _NOEXCEPT { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + DEBUG("DEC: %p refcnt=%zu rethrown=%d\n", thrown_object, + exception_header->referenceCount, exception_header->rethrown); + assert(exception_header->referenceCount > 0); + if (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0 && !exception_header->rethrown) + { + DEBUG("DEL: %p\n", thrown_object); + if (NULL != exception_header->exceptionDestructor) + exception_header->exceptionDestructor(thrown_object); + __cxa_free_exception(thrown_object); + } + } } -} +} // extern "C" -#endif // !__USING_WASM_EXCEPTIONS__ +} // abi diff --git a/system/lib/libcxxabi/src/cxa_exception_js_utils.cpp b/system/lib/libcxxabi/src/cxa_exception_js_utils.cpp new file mode 100644 index 0000000000000..e2c61aa0967e5 --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_exception_js_utils.cpp @@ -0,0 +1,105 @@ +#include "cxxabi.h" + +#include "cxa_exception.h" +#include "private_typeinfo.h" +#include +// #include +// #include +#include + +namespace __cxxabiv1 { + +// Utility routines +static +inline +__cxa_exception* +cxa_exception_from_thrown_object(void* thrown_object) +{ + return static_cast<__cxa_exception*>(thrown_object) - 1; +} + +// Note: This is never called when exception_header is masquerading as a +// __cxa_dependent_exception. +static +inline +void* +thrown_object_from_cxa_exception(__cxa_exception* exception_header) +{ + return static_cast(exception_header + 1); +} + +// Get the exception object from the unwind pointer. +// Relies on the structure layout, where the unwind pointer is right in +// front of the user's exception object +static inline __cxa_exception* cxa_exception_from_unwind_exception( + _Unwind_Exception* unwind_exception) { + return cxa_exception_from_thrown_object(unwind_exception + 1); +} + +extern "C" { + +void* __thrown_object_from_unwind_exception( + _Unwind_Exception* unwind_exception) { + __cxa_exception* exception_header = + cxa_exception_from_unwind_exception(unwind_exception); + return thrown_object_from_cxa_exception(exception_header); +} + +// Given a thrown_object, puts the information about its type and message into +// 'type' and 'message' output parameters. 'type' will contain the string +// representation of the type of the exception, e.g., 'int'. 'message' will +// contain the result of 'std::exception::what()' method if the type of the +// exception is a subclass of std::exception; otherwise it will be NULL. The +// caller is responsible for freeing 'type' buffer and also 'message' buffer, if +// it is not NULL. +void __get_exception_message(void* thrown_object, char** type, char** message) { + __cxa_exception* exception_header = + cxa_exception_from_thrown_object(thrown_object); + const __shim_type_info* thrown_type = + static_cast(exception_header->exceptionType); + const char* type_name = thrown_type->name(); + + int status = 0; + char* demangled_buf = __cxa_demangle(type_name, 0, 0, &status); + if (status == 0 && demangled_buf) { + *type = demangled_buf; + } else { + if (demangled_buf) { + free(demangled_buf); + } + *type = (char*)malloc(strlen(type_name) + 1); + strcpy(*type, type_name); + } + + *message = NULL; + const __shim_type_info* catch_type = + static_cast(&typeid(std::exception)); + int can_catch = catch_type->can_catch(thrown_type, thrown_object); + if (can_catch) { + const char* what = + static_cast(thrown_object)->what(); + *message = (char*)malloc(strlen(what) + 1); + strcpy(*message, what); + } +} + +// Returns a message saying that execution was terminated due to an exception. +// This message is freshly malloc'd and should be freed. +char* __get_exception_terminate_message(void* thrown_object) { + char* type; + char* message; + __get_exception_message(thrown_object, &type, &message); + char* result; + if (message != NULL) { + asprintf( + &result, "terminating with uncaught exception %s: %s", type, message); + free(message); + } else { + asprintf(&result, "terminating with uncaught exception of type %s", type); + } + free(type); + return result; +} +} // extern "C" + +} // namespace __cxxabiv1 diff --git a/system/lib/libcxxabi/src/cxa_noexception.cpp b/system/lib/libcxxabi/src/cxa_noexception.cpp index 4a803f72e193e..1097f75866983 100644 --- a/system/lib/libcxxabi/src/cxa_noexception.cpp +++ b/system/lib/libcxxabi/src/cxa_noexception.cpp @@ -49,6 +49,17 @@ __cxa_uncaught_exception() throw() { return false; } unsigned int __cxa_uncaught_exceptions() throw() { return 0; } +#if __EMSCRIPTEN__ +// Under emscripten this code is also linked when building when +// DISABLE_EXCEPTION_CATCHING is set but DISABLE_EXCEPTION_THROWING is not. +// TODO(sbc): Perhaps just call std::terminate here. It could +// just be some test code that needs updating. +void *__cxa_allocate_exception(size_t thrown_size) _NOEXCEPT { + char* allocation = (char*)malloc(thrown_size + sizeof(__cxa_exception)); + return allocation + sizeof(__cxa_exception); +} +#endif + } // extern "C" // provide dummy implementations for the 'no exceptions' case. diff --git a/system/lib/standalone/standalone.c b/system/lib/standalone/standalone.c index 7f2185e8da8da..43a446e14fd78 100644 --- a/system/lib/standalone/standalone.c +++ b/system/lib/standalone/standalone.c @@ -171,11 +171,6 @@ void __cxa_throw(void* ptr, void* type, void* destructor) { abort(); } -__attribute__((__weak__)) -void* __cxa_allocate_exception(size_t thrown_size) { - abort(); -} - // WasmFS integration. We stub out file preloading and such, that are not // expected to work anyhow. diff --git a/test/core/test_exceptions_primary.cpp b/test/core/test_exceptions_primary.cpp index 511b7eafa357d..70c35edbd6826 100644 --- a/test/core/test_exceptions_primary.cpp +++ b/test/core/test_exceptions_primary.cpp @@ -9,6 +9,7 @@ #include // std::logic_error int main () { + std::cout << "in main\n"; std::exception_ptr p; try { throw std::logic_error("some logic_error exception"); // throws @@ -20,7 +21,7 @@ int main () { std::cout << "(after exception)\n"; try { - std::rethrow_exception (p); + std::rethrow_exception(p); } catch (const std::exception& e) { std::cout << "exception caught: " << e.what() << '\n'; } diff --git a/test/no_this_in_dyncall.out b/test/no_this_in_dyncall.out index f70d7bba4ae1f..d81cc0710eb6c 100644 --- a/test/no_this_in_dyncall.out +++ b/test/no_this_in_dyncall.out @@ -1 +1 @@ -42 \ No newline at end of file +42 diff --git a/test/other/metadce/test_metadce_cxx_except.exports b/test/other/metadce/test_metadce_cxx_except.exports index 3f04dd4390620..0697b34554a59 100644 --- a/test/other/metadce/test_metadce_cxx_except.exports +++ b/test/other/metadce/test_metadce_cxx_except.exports @@ -1,5 +1,7 @@ __cxa_can_catch +__cxa_decrement_exception_refcount __cxa_free_exception +__cxa_increment_exception_refcount __cxa_is_pointer_type __errno_location __indirect_function_table diff --git a/test/other/metadce/test_metadce_cxx_except.jssize b/test/other/metadce/test_metadce_cxx_except.jssize index 76cb23dce88c3..556009c26d005 100644 --- a/test/other/metadce/test_metadce_cxx_except.jssize +++ b/test/other/metadce/test_metadce_cxx_except.jssize @@ -1 +1 @@ -30731 +30647 diff --git a/test/other/metadce/test_metadce_cxx_except.size b/test/other/metadce/test_metadce_cxx_except.size index 1dabda97be1a0..e002babd75bc5 100644 --- a/test/other/metadce/test_metadce_cxx_except.size +++ b/test/other/metadce/test_metadce_cxx_except.size @@ -1 +1 @@ -165312 +166229 diff --git a/test/other/metadce/test_metadce_cxx_mangle.exports b/test/other/metadce/test_metadce_cxx_mangle.exports index d517a701b28ff..fad2ef2b8381c 100644 --- a/test/other/metadce/test_metadce_cxx_mangle.exports +++ b/test/other/metadce/test_metadce_cxx_mangle.exports @@ -1,6 +1,11 @@ __cxa_can_catch +__cxa_decrement_exception_refcount __cxa_demangle +<<<<<<< HEAD:test/other/metadce/test_metadce_cxx_mangle.exports __cxa_free_exception +======= +__cxa_increment_exception_refcount +>>>>>>> 3efb14451 (Move parts of emscripten exception handling to native code. NFC):tests/other/metadce/hello_libcxx_O2_fexceptions_DEMANGLE_SUPPORT.exports __cxa_is_pointer_type __errno_location __indirect_function_table diff --git a/test/other/metadce/test_metadce_cxx_noexcept.exports b/test/other/metadce/test_metadce_cxx_noexcept.exports index 7590b6e0dd3f1..f4dc65925fb0b 100644 --- a/test/other/metadce/test_metadce_cxx_noexcept.exports +++ b/test/other/metadce/test_metadce_cxx_noexcept.exports @@ -1,3 +1,4 @@ +__cxa_increment_exception_refcount __cxa_is_pointer_type __errno_location __indirect_function_table diff --git a/test/other/metadce/test_metadce_cxx_noexcept.jssize b/test/other/metadce/test_metadce_cxx_noexcept.jssize index 5a83e94204ad0..a17c777bce2e8 100644 --- a/test/other/metadce/test_metadce_cxx_noexcept.jssize +++ b/test/other/metadce/test_metadce_cxx_noexcept.jssize @@ -1 +1 @@ -26205 +26122 diff --git a/test/other/metadce/test_metadce_cxx_noexcept.size b/test/other/metadce/test_metadce_cxx_noexcept.size index f4b7350835a3f..79e609efbb1cd 100644 --- a/test/other/metadce/test_metadce_cxx_noexcept.size +++ b/test/other/metadce/test_metadce_cxx_noexcept.size @@ -1 +1 @@ -126251 +126371 diff --git a/test/other/metadce/test_metadce_minimal_O0.jssize b/test/other/metadce/test_metadce_minimal_O0.jssize index 4146d1bfbc386..6121d7203d2bf 100644 --- a/test/other/metadce/test_metadce_minimal_O0.jssize +++ b/test/other/metadce/test_metadce_minimal_O0.jssize @@ -1 +1 @@ -20294 +20694 diff --git a/test/test_core.py b/test/test_core.py index 594299cc458a0..cd72c21e9dd77 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -1,7 +1,3 @@ -# Copyright 2013 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. import hashlib import json @@ -1262,7 +1258,6 @@ def test_exceptions_off(self): @no_asan('TODO: ASan support in minimal runtime') @no_wasm64('MEMORY64 does not yet support exceptions') def test_exceptions_minimal_runtime(self): - self.set_setting('EXCEPTION_DEBUG') self.set_setting('EXIT_RUNTIME') self.maybe_closure() self.set_setting('MINIMAL_RUNTIME') @@ -1273,6 +1268,7 @@ def test_exceptions_minimal_runtime(self): self.set_setting('DISABLE_EXCEPTION_CATCHING', 0) self.do_run_from_file(test_file('core/test_exceptions.cpp'), test_file('core/test_exceptions_caught.out')) + self.set_setting('EXCEPTION_DEBUG') self.set_setting('DISABLE_EXCEPTION_CATCHING') self.do_run_from_file(test_file('core/test_exceptions.cpp'), test_file('core/test_exceptions_uncaught.out'), assert_returncode=NON_ZERO) diff --git a/tools/deps_info.py b/tools/deps_info.py index 4d5f9a557019b..1bc56aa76a819 100644 --- a/tools/deps_info.py +++ b/tools/deps_info.py @@ -185,18 +185,16 @@ def append_deps_info(js_symbol_deps): def get_deps_info(): if not settings.WASM_EXCEPTIONS and settings.LINK_AS_CXX: - _deps_info['__cxa_begin_catch'] = ['__cxa_is_pointer_type', '__cxa_free_exception'] - _deps_info['__cxa_throw'] = ['__cxa_is_pointer_type'] - _deps_info['__cxa_find_matching_catch'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_1'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_2'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_3'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_4'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_5'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_6'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_7'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_8'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_9'] = ['__cxa_can_catch', 'setTempRet0'] + _deps_info['__cxa_end_catch'] = ['setThrew', '__cxa_decrement_exception_refcount'] + base_js_exception_deps = [ + '__cxa_is_pointer_type', + '__cxa_increment_exception_refcount', + ] + _deps_info['__cxa_throw'] = base_js_exception_deps + _deps_info['__cxa_begin_catch'] = base_js_exception_deps + _deps_info['__cxa_find_matching_catch'] = base_js_exception_deps + ['__cxa_can_catch', 'setTempRet0'] + for i in range(1, 10): + _deps_info['__cxa_find_matching_catch_%d' % i] = _deps_info['__cxa_find_matching_catch'] if settings.PTHREADS and settings.OFFSCREENCANVAS_SUPPORT: _deps_info['pthread_create'] = ['malloc'] if settings.FILESYSTEM and settings.SYSCALLS_REQUIRE_FILESYSTEM: diff --git a/tools/system_libs.py b/tools/system_libs.py index b0cca903a4963..8098052fbbba8 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -1476,16 +1476,20 @@ def get_files(self): 'stdlib_stdexcept.cpp', 'stdlib_typeinfo.cpp', 'private_typeinfo.cpp', - 'cxa_exception_emscripten.cpp', + 'cxa_exception_js_utils.cpp', ] if self.eh_mode == Exceptions.NONE: filenames += ['cxa_noexception.cpp'] + elif self.eh_mode == Exceptions.EMSCRIPTEN: + filenames += ['cxa_exception_emscripten.cpp'] elif self.eh_mode == Exceptions.WASM: filenames += [ 'cxa_exception_storage.cpp', 'cxa_exception.cpp', 'cxa_personality.cpp' ] + else: + assert False return files_in_path( path='system/lib/libcxxabi/src', From 45117adeaabc1adc33523157c9d1eac74f374b12 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Mon, 27 Mar 2023 22:43:47 +0100 Subject: [PATCH 2/5] Rebaseline test_metadce_cxx_mangle.exports --- test/other/metadce/test_metadce_cxx_mangle.exports | 3 --- test/other/metadce/test_metadce_cxx_mangle.jssize | 2 +- test/other/metadce/test_metadce_cxx_mangle.size | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/test/other/metadce/test_metadce_cxx_mangle.exports b/test/other/metadce/test_metadce_cxx_mangle.exports index fad2ef2b8381c..2a14e2cb1fbda 100644 --- a/test/other/metadce/test_metadce_cxx_mangle.exports +++ b/test/other/metadce/test_metadce_cxx_mangle.exports @@ -1,11 +1,8 @@ __cxa_can_catch __cxa_decrement_exception_refcount __cxa_demangle -<<<<<<< HEAD:test/other/metadce/test_metadce_cxx_mangle.exports __cxa_free_exception -======= __cxa_increment_exception_refcount ->>>>>>> 3efb14451 (Move parts of emscripten exception handling to native code. NFC):tests/other/metadce/hello_libcxx_O2_fexceptions_DEMANGLE_SUPPORT.exports __cxa_is_pointer_type __errno_location __indirect_function_table diff --git a/test/other/metadce/test_metadce_cxx_mangle.jssize b/test/other/metadce/test_metadce_cxx_mangle.jssize index f27a5fc2f515d..0f8b4a91ce346 100644 --- a/test/other/metadce/test_metadce_cxx_mangle.jssize +++ b/test/other/metadce/test_metadce_cxx_mangle.jssize @@ -1 +1 @@ -30736 +30651 diff --git a/test/other/metadce/test_metadce_cxx_mangle.size b/test/other/metadce/test_metadce_cxx_mangle.size index 66fd4fb1993fd..50931b6ce6811 100644 --- a/test/other/metadce/test_metadce_cxx_mangle.size +++ b/test/other/metadce/test_metadce_cxx_mangle.size @@ -1 +1 @@ -220408 +221325 From 9e0f9a110d2487d11ca9ed9253d2eebdf4476983 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 28 Mar 2023 13:40:44 +0100 Subject: [PATCH 3/5] Few fixups --- system/lib/libcxxabi/src/cxa_exception_emscripten.cpp | 2 +- test/other/metadce/test_metadce_cxx_ctors1.exports | 1 + test/other/metadce/test_metadce_cxx_ctors2.exports | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp b/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp index 8f74073678e88..af7ec0c0452f0 100644 --- a/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp +++ b/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp @@ -1,4 +1,4 @@ -//===------------------------- cxa_emscripten.cpp -------------------------===// +//===-------------------- cxa_exception_emscripten.cpp --------------------===// // // Most code in the file is directly copied from cxa_exception.cpp. // TODO(sbc): consider merging them diff --git a/test/other/metadce/test_metadce_cxx_ctors1.exports b/test/other/metadce/test_metadce_cxx_ctors1.exports index 7590b6e0dd3f1..f4dc65925fb0b 100644 --- a/test/other/metadce/test_metadce_cxx_ctors1.exports +++ b/test/other/metadce/test_metadce_cxx_ctors1.exports @@ -1,3 +1,4 @@ +__cxa_increment_exception_refcount __cxa_is_pointer_type __errno_location __indirect_function_table diff --git a/test/other/metadce/test_metadce_cxx_ctors2.exports b/test/other/metadce/test_metadce_cxx_ctors2.exports index 0605207136d30..183246153c0dd 100644 --- a/test/other/metadce/test_metadce_cxx_ctors2.exports +++ b/test/other/metadce/test_metadce_cxx_ctors2.exports @@ -1,3 +1,4 @@ +__cxa_increment_exception_refcount __cxa_is_pointer_type __errno_location __indirect_function_table From 6dcc5babcdc349f604641e8c7461b44b9f070689 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 28 Mar 2023 13:49:04 +0100 Subject: [PATCH 4/5] Use -fexceptions for test_rust_gxx_personality_v0 IIUC, the original issue was about Rust referencing this symbol in panic=unwind mode, which is using Emscripten's exceptions, but the test was verifying that the symbol exists even with exceptions disabled. Now that the two modes are better separated, the test needs to be updated. --- test/test_other.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_other.py b/test/test_other.py index 7d70fb9c13768..e40d929cdb118 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -13011,7 +13011,7 @@ def test_rust_gxx_personality_v0(self): return 0; } } - ''', assert_returncode=NON_ZERO) + ''', assert_returncode=NON_ZERO, emcc_args=['-fexceptions']) def test_bigint64array_polyfill(self): bigint64array = read_file(path_from_root('src/polyfill/bigint64array.js')) From ad91df71564bd2ccaf71d93bd052534439b782fd Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 29 Mar 2023 23:41:10 +0100 Subject: [PATCH 5/5] Revert some parts --- src/jsifier.js | 8 ++++---- test/test_core.py | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/jsifier.js b/src/jsifier.js index 273203082723d..a966e7083cc05 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -308,13 +308,13 @@ function ${name}(${args}) { warn('To build in STANDALONE_WASM mode without a main(), use emcc --no-entry'); } } - // We have already warned/errored about this function, so for the purposes of Closure use, mute all type checks - // regarding this function, marking ot a variadic function that can take in anything and return anything. - // (not useful to warn/error multiple times) - LibraryManager.library[symbol + '__docs'] = '/** @type {function(...*):?} */'; if (!RELOCATABLE) { // emit a stub that will fail at runtime LibraryManager.library[symbol] = new Function(`err('missing function: ${symbol}'); abort(-1);`); + // We have already warned/errored about this function, so for the purposes of Closure use, mute all type checks + // regarding this function, marking ot a variadic function that can take in anything and return anything. + // (not useful to warn/error multiple times) + LibraryManager.library[symbol + '__docs'] = '/** @type {function(...*):?} */'; } else { // Create a stub for this symbol which can later be replaced by the // dynamic linker. If this stub is called before the symbol is diff --git a/test/test_core.py b/test/test_core.py index cd72c21e9dd77..dec35b9f46535 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -1,3 +1,7 @@ +# Copyright 2013 The Emscripten Authors. All rights reserved. +# Emscripten is available under two separate licenses, the MIT license and the +# University of Illinois/NCSA Open Source License. Both these licenses can be +# found in the LICENSE file. import hashlib import json