Skip to content

Commit 3daf0f0

Browse files
authored
[EH] Don't export unnecessary functions for EH when not used (emscripten-core#26493)
When we enabled (Wasm / Emscripten) EH and didn't use it, we still pulled in a lot of JS library functions + libc++abi library functions that were not DCE'ed. The reason was we mandatorily exported many functions whenever some EH options were enabled, regardless of whether they were used or not. 1. We used to enable `EXPORT_EXCEPTION_HANDLING_HELPERS`, which exports `getExceptionMessage` and `in/decrementExceptionRefcount`, whenever `EXCEPTION_STACK_TRACES` was true. And `EXCEPTION_STACK_TRACES` is true whenever `ASSERTIONS` is true, which is the default at `-O0`. And those exported functions can pull in many libc++abi functions. As a result, at `-O0`, we pulled in a lot of functions even when we were not using any exceptions. This PR removes that `EXCEPTION_STACK_TRACES` -> `EXPORT_EXCEPTION_HANDLING_HELPERS` link. Without this link, we can still see stack traces with exception messages with no problem, because `__cxa_throw` -> `__throw_exception_with_stack_trace` -> `getExceptionMessage` dependencies: https://github.com/emscripten-core/emscripten/blob/6ad2f5e03021a39377428e9d476985fc967014d4/system/lib/libcxxabi/src/cxa_exception.cpp#L302-L304 https://github.com/emscripten-core/emscripten/blob/6ad2f5e03021a39377428e9d476985fc967014d4/src/lib/libexceptions.js#L311 2. If we do 1, Emscripten EH's `getExceptionMessage` does not work, because unlike Wasm EH, its `getExceptionMessage` dependency is within `runtime_exceptions.js`, where we can't attach `__deps`, so we can't track it: https://github.com/emscripten-core/emscripten/blob/6ad2f5e03021a39377428e9d476985fc967014d4/src/runtime_exceptions.js#L20 So, this adds `getExceptionMessage` as a dependency of `libexception.js`'s `__cxa_throw` directly, when `EXCEPTION_STACK_TRACES` is enabled. 3. This removes several functions from `REQUIRED_EXPORTS` when Emscripten EH is used. Previously, the comment said, in LTO mode, `_cxa_find_matching_catch_*` -> `__cxa_can_catch` dependency was not tracked. But now, in Emscripten EH, we preemptively add several `__cxa_find_matching_catch_n`s, and it depends on `findMatchingCatch`, which depends on `__cxa_end_catch`: https://github.com/emscripten-core/emscripten/blob/78403050f355085104175499224ad0f6bccb5fb1/src/lib/libexceptions.js#L371-L405 https://github.com/emscripten-core/emscripten/blob/78403050f355085104175499224ad0f6bccb5fb1/src/lib/libexceptions.js#L217 So we don't need to require exporting `__cxa_end_catch` anymore. Also, other required exports (`__cxa_in/decrement_exception_count` and `__cxa_free_exceptions`) are dependencies that can be naturally referred within libc++abi. I think these were added here in emscripten-core#16627 when we were using `deps_info.py`, which we don't use anymore. After this commit, when you compile an empty `int main { return 0; }` with `-O0` and `-fexceptions`/`-fwasm-exceptions`, the size decreases to: - `-fexceptions`: 113212 -> 1168 - `-fwasm-exceptions`: 109177 -> 1106
1 parent 8f9388a commit 3daf0f0

File tree

5 files changed

+32
-32
lines changed

5 files changed

+32
-32
lines changed

src/lib/libexceptions.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ var LibraryExceptions = {
8585
__cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount',
8686
#if !DISABLE_EXCEPTION_CATCHING
8787
'__cxa_increment_exception_refcount',
88+
#endif
89+
#if EXCEPTION_STACK_TRACES
90+
// When EXCEPTION_STACK_TRACES is enabled, storeException contains a call to
91+
// 'new CppException', whose constructor calls getExceptionMessage. We can't
92+
// track the dependency there, so we track it here.
93+
'$getExceptionMessage',
8894
#endif
8995
],
9096
__cxa_throw: (ptr, type, destructor) => {

test/codesize/test_codesize_cxx_except.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"a.out.js": 22875,
33
"a.out.js.gz": 8956,
4-
"a.out.nodebug.wasm": 172540,
5-
"a.out.nodebug.wasm.gz": 57452,
6-
"total": 195415,
7-
"total_gz": 66408,
4+
"a.out.nodebug.wasm": 172516,
5+
"a.out.nodebug.wasm.gz": 57438,
6+
"total": 195391,
7+
"total_gz": 66394,
88
"sent": [
99
"__cxa_begin_catch",
1010
"__cxa_end_catch",
@@ -87,7 +87,6 @@
8787
"exports": [
8888
"__cxa_can_catch",
8989
"__cxa_decrement_exception_refcount",
90-
"__cxa_free_exception",
9190
"__cxa_get_exception_ptr",
9291
"__cxa_increment_exception_refcount",
9392
"__indirect_function_table",

test/codesize/test_codesize_cxx_mangle.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"a.out.js": 22925,
33
"a.out.js.gz": 8978,
4-
"a.out.nodebug.wasm": 238981,
5-
"a.out.nodebug.wasm.gz": 79855,
6-
"total": 261906,
7-
"total_gz": 88833,
4+
"a.out.nodebug.wasm": 238957,
5+
"a.out.nodebug.wasm.gz": 79847,
6+
"total": 261882,
7+
"total_gz": 88825,
88
"sent": [
99
"__cxa_begin_catch",
1010
"__cxa_end_catch",
@@ -88,7 +88,6 @@
8888
"__cxa_can_catch",
8989
"__cxa_decrement_exception_refcount",
9090
"__cxa_demangle",
91-
"__cxa_free_exception",
9291
"__cxa_get_exception_ptr",
9392
"__cxa_increment_exception_refcount",
9493
"__indirect_function_table",

test/test_other.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8692,6 +8692,20 @@ def test_multi_inheritance_exception_message(self):
86928692
self.set_setting('ASSERTIONS')
86938693
self.do_runf('src.cpp', 'ERROR\n')
86948694

8695+
@with_all_eh_sjlj
8696+
def test_unused_eh_dce(self):
8697+
# Programs that don't use exceptions should not pull in libc++abi or
8698+
# exception formatting helpers when compiled with -fexceptions /
8699+
# -fwasm-exceptions. We verify this by ensuring the output Wasm binary is
8700+
# small.
8701+
create_file('main.cpp', 'int main() { return 0; }')
8702+
self.do_runf('main.cpp', '', cflags=['-O0'])
8703+
8704+
self.assertExists('main.wasm')
8705+
size = os.path.getsize('main.wasm')
8706+
# 3KB is a very generous upper bound for an empty program without libc++abi
8707+
self.assertLess(size, 3000)
8708+
86958709
@requires_node
86968710
def test_jsrun(self):
86978711
engine = self.get_current_js_engine()

tools/link.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,27 +1703,11 @@ def limit_incoming_module_api():
17031703
settings.REQUIRED_EXPORTS += ['realloc']
17041704
options.post_js.append(utils.path_from_root('src/build_as_worker.js'))
17051705

1706+
# Emscripten exception handling can generate invoke calls, and they call
1707+
# setThrew(). We cannot handle this using deps_info as the invokes are not
1708+
# emitted because of library function usage, but by codegen itself.
17061709
if not settings.DISABLE_EXCEPTION_CATCHING:
1707-
settings.REQUIRED_EXPORTS += [
1708-
# For normal builds the entries in deps_info.py are enough to include
1709-
# these symbols whenever __cxa_find_matching_catch_* functions are
1710-
# found. However, under LTO these symbols don't exist prior to linking
1711-
# so we include then unconditionally when exceptions are enabled.
1712-
'__cxa_can_catch',
1713-
1714-
# __cxa_begin_catch depends on this but we can't use deps info in this
1715-
# case because that only works for user-level code, and __cxa_begin_catch
1716-
# can be used by the standard library.
1717-
'__cxa_increment_exception_refcount',
1718-
# Same for __cxa_end_catch
1719-
'__cxa_decrement_exception_refcount',
1720-
1721-
# Emscripten exception handling can generate invoke calls, and they call
1722-
# setThrew(). We cannot handle this using deps_info as the invokes are not
1723-
# emitted because of library function usage, but by codegen itself.
1724-
'setThrew',
1725-
'__cxa_free_exception',
1726-
]
1710+
settings.REQUIRED_EXPORTS += ['setThrew']
17271711

17281712
if settings.ASYNCIFY:
17291713
if not settings.ASYNCIFY_IGNORE_INDIRECT:
@@ -1812,8 +1796,6 @@ def get_full_import_name(name):
18121796
# errors out.
18131797
if settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS:
18141798
exit_with_error('EXCEPTION_STACK_TRACES requires either of -fexceptions or -fwasm-exceptions')
1815-
# EXCEPTION_STACK_TRACES implies EXPORT_EXCEPTION_HANDLING_HELPERS
1816-
settings.EXPORT_EXCEPTION_HANDLING_HELPERS = True
18171799

18181800
# Make `getExceptionMessage` and other necessary functions available for use.
18191801
if settings.EXPORT_EXCEPTION_HANDLING_HELPERS:

0 commit comments

Comments
 (0)