Skip to content

Commit

Permalink
Move emscripten exception handling code to native
Browse files Browse the repository at this point in the history
  • Loading branch information
sbc100 committed Aug 19, 2021
1 parent ff75baa commit 9aca4d4
Show file tree
Hide file tree
Showing 14 changed files with 95 additions and 132 deletions.
2 changes: 2 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2237,6 +2237,8 @@ def check_memory_setting(setting):
# so we include then unconditionally when exceptions are enabled.
'___cxa_is_pointer_type',
'___cxa_can_catch',
'___cxa_increment_exception_refcount',
'___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
Expand Down
120 changes: 11 additions & 109 deletions src/library_exceptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@ var LibraryExceptions = {
{{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionDestructor, 'destructor', '*') }}};
};

this.get_destructor = function() {
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') }}};
Expand All @@ -67,33 +59,7 @@ var LibraryExceptions = {
this.init = function(type, destructor) {
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 USE_PTHREADS
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 USE_PTHREADS
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;
};
},

$CatchInfo__deps: ['$ExceptionInfo', '__cxa_is_pointer_type'],
Expand Down Expand Up @@ -160,80 +126,13 @@ var LibraryExceptions = {
}
},

$exception_addRef: function (info) {
#if EXCEPTION_DEBUG
err('addref ' + 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
err('decref ' + 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('ii', 'destructor') }}}(info.excPtr);
}
___cxa_free_exception(info.excPtr);
#if EXCEPTION_DEBUG
err('decref freeing exception ' + [info.excPtr, exceptionLast, 'stack', exceptionCaught]);
#endif
}
},

// Exceptions
__cxa_allocate_exception__sig: 'vi',
__cxa_allocate_exception: function(size) {
// Thrown object is prepended by exception metadata block
return _malloc(size + {{{ C_STRUCTS.__cxa_exception.__size__ }}}) + {{{ C_STRUCTS.__cxa_exception.__size__ }}};
},

__cxa_free_exception__deps: ['$ExceptionInfo'],
__cxa_free_exception__sig: 'vi',
__cxa_free_exception: function(ptr) {
#if ABORTING_MALLOC || ASSERTIONS
try {
#endif
return _free(new ExceptionInfo(ptr).ptr);
#if ABORTING_MALLOC || ASSERTIONS
} catch(e) {
#if ASSERTIONS
err('exception during cxa_free_exception: ' + e);
#endif
}
#endif
},

__cxa_increment_exception_refcount__deps: ['$exception_addRef', '$ExceptionInfo'],
__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: 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: 'viii',
__cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount'],
__cxa_throw: function(ptr, type, destructor) {
#if EXCEPTION_DEBUG
err('Compiled code throwing an exception, ' + [ptr,type,destructor]);
err('Compiled code throwing an exception, ' + [ptr.toString(16),type,destructor]);
#endif
var info = new ExceptionInfo(ptr);
// Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception.
Expand Down Expand Up @@ -276,8 +175,9 @@ var LibraryExceptions = {
return type;
},

__cxa_begin_catch__deps: ['$CatchInfo', '$exceptionCaught', '$exception_addRef',
'$uncaughtExceptionCount'],
__cxa_begin_catch__deps: ['$CatchInfo', '$exceptionCaught',
'$uncaughtExceptionCount',
'__cxa_increment_exception_refcount'],
__cxa_begin_catch: function(ptr) {
var catchInfo = new CatchInfo(ptr);
var info = catchInfo.get_exception_info();
Expand All @@ -290,15 +190,15 @@ var LibraryExceptions = {
#if EXCEPTION_DEBUG
err('cxa_begin_catch ' + [ptr, 'stack', exceptionCaught]);
#endif
exception_addRef(info);
___cxa_increment_exception_refcount(catchInfo.get_base_ptr());
return catchInfo.get_exception_ptr();
},

// We're done with a catch. Now, we can run the destructor if there is one
// 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',
'$CatchInfo'],
__cxa_end_catch__sig: 'v',
__cxa_end_catch: function() {
Expand All @@ -313,7 +213,7 @@ var LibraryExceptions = {
#if EXCEPTION_DEBUG
err('cxa_end_catch popped ' + [catchInfo, exceptionLast, 'stack', exceptionCaught]);
#endif
exception_decRef(catchInfo.get_exception_info());
___cxa_decrement_exception_refcount(catchInfo.get_base_ptr());
catchInfo.free();
exceptionLast = 0; // XXX in decRef?
},
Expand All @@ -339,13 +239,15 @@ var LibraryExceptions = {
throw exception;
},

__cxa_current_primary_exception__deps: ['$exceptionCaught', '$exception_addRef', '$CatchInfo'],
__cxa_current_primary_exception__deps: ['$exceptionCaught',
'__cxa_increment_exception_refcount',
'$CatchInfo'],
__cxa_current_primary_exception: function() {
if (!exceptionCaught.length) {
return 0;
}
var catchInfo = exceptionCaught[exceptionCaught.length - 1];
exception_addRef(catchInfo.get_exception_info());
___cxa_increment_exception_refcount(catchInfo.get_base_ptr());
return catchInfo.get_base_ptr();
},

Expand Down
5 changes: 3 additions & 2 deletions system/lib/libcxxabi/src/cxa_exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@

namespace __cxxabiv1 {

#ifdef __USING_EMSCRIPTEN_EXCEPTIONS__
#if defined(__EMSCRIPTEN__) && !defined(__USING_WASM_EXCEPTIONS__)

struct _LIBCXXABI_HIDDEN __cxa_exception {
size_t referenceCount;
std::type_info *exceptionType;
void (*exceptionDestructor)(void *);
// In Wasm, destructors return 'this' as in ARM
void *(*exceptionDestructor)(void *);
uint8_t caught;
uint8_t rethrown;
};
Expand Down
61 changes: 61 additions & 0 deletions system/lib/libcxxabi/src/cxa_exception_emscripten.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include <cstring>
#include <cstdio>

#include "cxxabi.h"

#include "cxa_exception.h"
#include "include/atomic_support.h"

namespace __cxxabiv1 {

extern "C" {

// Utility routines
static inline __cxa_exception* cxa_exception_from_thrown_object(void* thrown_object) {
return static_cast<__cxa_exception*>(thrown_object) - 1;
}

void *__cxa_allocate_exception(size_t thrown_size) _NOEXCEPT {
size_t actual_size = sizeof(__cxa_exception) + thrown_size;
char* buffer = (char*)::malloc(actual_size);
::memset(buffer, 0, actual_size);
//printf("__cxa_allocate_exception obj=%p header=%p\n", buffer + sizeof(__cxa_exception), buffer);
return buffer + sizeof(__cxa_exception);
}

void __cxa_free_exception(void *thrown_object) _NOEXCEPT {
__cxa_exception* exception = cxa_exception_from_thrown_object(thrown_object);
//printf("__cxa_free_exception obj=%p header=%p\n", thrown_object, exception);
::free(exception);
}

#ifdef __USING_EMSCRIPTEN_EXCEPTIONS__
void __cxa_increment_exception_refcount(void *thrown_object) _NOEXCEPT {
if (thrown_object != NULL) {
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
//printf("__cxa_increment_exception_refcount obj=%p header=%p\n", thrown_object, exception_header);
std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1));
}
}

_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);
//printf("__cxa_decrement_exception_refcount obj=%p header=%p rethrown=%d\n", thrown_object, exception_header, exception_header->rethrown);
// 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 (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0 && !exception_header->rethrown) {
//printf("__cxa_decrement_exception_refcount destructor=%p\n", exception_header->exceptionDestructor);
if (NULL != exception_header->exceptionDestructor)
exception_header->exceptionDestructor(thrown_object);
__cxa_free_exception(thrown_object);
//err('decref freeing exception ' + [info.excPtr, exceptionLast, 'stack', exceptionCaught]);
}
}
}
#endif

}

} // abi
11 changes: 7 additions & 4 deletions system/lib/libcxxabi/src/private_typeinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
// is_equal() with use_strcmp=false so the string names are not compared.

#include <string.h>
#include <stdio.h>

#ifdef _LIBCXXABI_FORGIVING_DYNAMIC_CAST
#include "abort_message.h"
Expand Down Expand Up @@ -1337,9 +1338,11 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info,
extern "C" {

int __cxa_can_catch(__shim_type_info* catchType, __shim_type_info* excpType, void **thrown) {
//std::type_info *t1 = static_cast<std::type_info*>(catchType);
//std::type_info *t2 = static_cast<std::type_info*>(excpType);
//printf("can %s catch %s (%p)?\n", t1->name(), t2->name(), thrown);
#if 0
std::type_info *t1 = static_cast<std::type_info*>(catchType);
std::type_info *t2 = static_cast<std::type_info*>(excpType);
printf("can %s catch %s (%p)?\n", t1->name(), t2->name(), thrown);
#endif

void *temp = *thrown;
int ret = catchType->can_catch(excpType, temp);
Expand All @@ -1352,6 +1355,6 @@ int __cxa_is_pointer_type(__shim_type_info* type) {
}

}
#endif // __USING_EMSCRIPTEN_EXCEPTIONS__
#endif // !__USING_WASM_EXCEPTIONS__

} // __cxxabiv1
4 changes: 0 additions & 4 deletions system/lib/standalone/standalone.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,3 @@ void
__cxa_throw(void* ptr, void* type, void* destructor) {
abort();
}

void* __cxa_allocate_exception(size_t thrown_size) {
abort();
}
2 changes: 2 additions & 0 deletions tests/other/metadce/hello_libcxx_O2_fexceptions.exports
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
__cxa_can_catch
__cxa_decrement_exception_refcount
__cxa_increment_exception_refcount
__cxa_is_pointer_type
__errno_location
__indirect_function_table
Expand Down
2 changes: 0 additions & 2 deletions tests/other/metadce/hello_libcxx_O2_fexceptions.imports
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
env.__cxa_allocate_exception
env.__cxa_begin_catch
env.__cxa_end_catch
env.__cxa_find_matching_catch_2
env.__cxa_find_matching_catch_3
env.__cxa_free_exception
env.__cxa_rethrow
env.__cxa_throw
env.__cxa_uncaught_exceptions
Expand Down
2 changes: 0 additions & 2 deletions tests/other/metadce/hello_libcxx_O2_fexceptions.sent
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
__cxa_allocate_exception
__cxa_atexit
__cxa_begin_catch
__cxa_end_catch
__cxa_find_matching_catch_2
__cxa_find_matching_catch_3
__cxa_free_exception
__cxa_rethrow
__cxa_throw
__cxa_uncaught_exceptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
__cxa_can_catch
__cxa_decrement_exception_refcount
__cxa_demangle
__cxa_increment_exception_refcount
__cxa_is_pointer_type
__errno_location
__indirect_function_table
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
env.__cxa_allocate_exception
env.__cxa_begin_catch
env.__cxa_end_catch
env.__cxa_find_matching_catch_2
env.__cxa_find_matching_catch_3
env.__cxa_free_exception
env.__cxa_rethrow
env.__cxa_throw
env.__cxa_uncaught_exceptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
__cxa_allocate_exception
__cxa_atexit
__cxa_begin_catch
__cxa_end_catch
__cxa_find_matching_catch_2
__cxa_find_matching_catch_3
__cxa_free_exception
__cxa_rethrow
__cxa_throw
__cxa_uncaught_exceptions
Expand Down
8 changes: 4 additions & 4 deletions tools/deps_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
'SDL_PushEvent': ['malloc', 'free'],
'SDL_free': ['free'],
'SDL_malloc': ['malloc', 'free'],
'__cxa_allocate_exception': ['malloc'],
'__cxa_end_catch': ['setThrew', 'free'],
'__cxa_free_exception': ['free'],
'__cxa_end_catch': ['setThrew', 'free', '__cxa_decrement_exception_refcount'],
'__cxa_current_primary_exception': ['__cxa_increment_exception_refcount'],
'__cxa_begin_catch': ['__cxa_increment_exception_refcount'],
'_embind_register_class': ['free'],
'_embind_register_enum_value': ['free'],
'_embind_register_function': ['free'],
Expand Down Expand Up @@ -201,7 +201,7 @@

def get_deps_info():
if not settings.EXCEPTION_HANDLING and settings.LINK_AS_CXX:
_deps_info['__cxa_begin_catch'] = ['__cxa_is_pointer_type']
_deps_info['__cxa_begin_catch'] = ['__cxa_is_pointer_type', '__cxa_increment_exception_refcount']
_deps_info['__cxa_find_matching_catch'] = ['__cxa_can_catch']
_deps_info['__cxa_find_matching_catch_1'] = ['__cxa_can_catch']
_deps_info['__cxa_find_matching_catch_2'] = ['__cxa_can_catch']
Expand Down
Loading

0 comments on commit 9aca4d4

Please sign in to comment.