[EH] Don't export unnecessary functions for EH when not used - #26493
Conversation
When we enabled (Wasm / Emscripten) EH and don'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
This is an automatic change generated by tools/maint/rebaseline_tests.py. The following (2) test expectation files were updated by running the tests with `--rebaseline`: ``` codesize/test_codesize_cxx_except.json: 195415 => 195391 [-24 bytes / -0.01%] codesize/test_codesize_cxx_mangle.json: 261906 => 261882 [-24 bytes / -0.01%] Average change: -0.01% (-0.01% - -0.01%) ```
| #if !DISABLE_EXCEPTION_CATCHING | ||
| '__cxa_increment_exception_refcount', | ||
| #endif | ||
| #if EXCEPTION_STACK_TRACES |
There was a problem hiding this comment.
Perhaps this dependency warrants a comment since its not directly called below in this function?
|
|
||
| @with_all_eh_sjlj | ||
| def test_c_program_eh_dce(self): | ||
| # Pure C programs compiled with -fexceptions / -fwasm-exceptions should not |
There was a problem hiding this comment.
Should it even be legal to link C programs with -fexceptions / -fwasm-exceptions?
Currently we do default C++ linking but I'm hoping to disable that soon by turning off LINK_AS_CXX by default.
There was a problem hiding this comment.
clang -fexceptions main.ccompiles without a problem, so I guess it should..?
By the way what does LINK_AS_CXX do? It is already false by default:
emscripten/src/settings_internal.js
Lines 213 to 214 in c624110
There was a problem hiding this comment.
I know it compiles without a problem today, but maybe it shouldn't?
Actually it looks like clang allows -fexceptions and -fno-exceptions for plain-old-c code so I guess this fine.
LINK_AS_CXX is set based on DEFAULT_TO_CXX which I'm hoping to disable by default soon. It means you will need to use em++ to link C++ programs just like you need to use g++ or clang++ today.
There was a problem hiding this comment.
OK, changed main.c to main.cpp in the test: 121100c
| # 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. | ||
| 'setThrew', |
There was a problem hiding this comment.
Since this is now just one element maybe move the comment outside and make this into single line of code
|
|
||
| @with_all_eh_sjlj | ||
| def test_c_program_eh_dce(self): | ||
| # Pure C programs compiled with -fexceptions / -fwasm-exceptions should not |
There was a problem hiding this comment.
Should it even be legal to link C programs with -fexceptions / -fwasm-exceptions?
Currently we do default C++ linking but I'm hoping to disable that soon by turning off LINK_AS_CXX by default.
I'm not saying we shouldn't land this as-is, but just wondering if its desirable to to make -fexceptions at leat not valid for C programs. In which case maybe this test could just use main.cpp instead?]
Maybe it should be called test_unused_eh_dce ?
There was a problem hiding this comment.
Should it even be legal to link C programs with
-fexceptions / -fwasm-exceptions?Currently we do default C++ linking but I'm hoping to disable that soon by turning off
LINK_AS_CXXby default.
I'm not saying we shouldn't land this as-is, but just wondering if its desirable to to make-fexceptionsat leat not valid for C programs. In which case maybe this test could just usemain.cppinstead?
This looks a duplication of #26493 (comment). I replied there.
Maybe it should be called
test_unused_eh_dce?
Done: 9e9b4a2
| if settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS: | ||
| exit_with_error('EXCEPTION_STACK_TRACES requires either of -fexceptions or -fwasm-exceptions') | ||
| # EXCEPTION_STACK_TRACES implies EXPORT_EXCEPTION_HANDLING_HELPERS | ||
| settings.EXPORT_EXCEPTION_HANDLING_HELPERS = True |
There was a problem hiding this comment.
Do we need to update any corresponding docs for this?
There was a problem hiding this comment.
Hmm, how about just removing EXPORT_EXCEPTION_HANDLING_HELPERS? Given that using throw (either in Emscripten or Wasm EH) exports getExceptionMessage anyway (this behavior was there; it hasn't changed in this PR), the only thing a user may additionally want to use from the JS side is decrementExceptionRefcount to avoid memory leaks.
Lines 1534 to 1548 in c624110
And currently
EXPORT_EXCEPTION_HANDLING_HELPERS exports three functions: getExceptionMessage, incrementExceptionRefcount, and decrementExceptionRefcount. Actually I can't think of any reason why a user would want to call incrementExceptionRefcount. It was there just because decrementExceptionRefcount was there and they looked like a pair.
How about adding those in/decrementExceptionRecount to __cxa_throw's deps when EXCEPTION_STACK_TRACES is on, like we did for getExceptionMessage here, and just removing EXPORT_EXCEPTION_HANDLING_HELPERS? I think one less EH option is better. The current docs say you need to turn EXPORT_EXCEPTION_HANDLING_HELPERS on to use getExceptionMessage (which is not true) but gives the test_core.py code example above (which contains decrementExceptionRefcount, which does require EXPORT_EXCEPTION_HANDLING_HELPERS). If we just remove this option, users can use all those functions whenever EXCEPTION_STACK_TRACES is on, without setting another option.
If you agree, I'll do it as a follow-up.
There was a problem hiding this comment.
I'm always a fan of removing settings. Followup makes sense yes.
| '__cxa_increment_exception_refcount', | ||
| #endif | ||
| #if EXCEPTION_STACK_TRACES | ||
| // When EXCEPTION_STACK_TRACES is enabled, storeEcxeption contains a call to |
This effectively removes `EXPORT_EXCEPTION_HANDLING_HELPERS` setting. This marks the setting as deprecated not to crash users' builds right away in case they are using it. Even though it still exists as a deprecated setting, setting it to true will not change anything. It used to export `getExceptionMessage` and a few more functions (`in/decrementexceptionRefCount`), but after emscripten-core#26493, `getExceptionMessage` is exported anyway when exceptions are used and either `-sASSERTIONS` or `-sEXCEPTION_STACK_TRACES` is set, which are set by default at `-O0`. For Wasm EH, the dependency is automatically detected. For Emscripten EH, we had to add `getExceptionMessage` to deps of `__cxa_throw`. This adds `in/decrementexceptionRefCount` to deps of `__cxa_throw` (for Emscripten EH) and `__throw_exception_with_stack_trace` (for Wasm EH) and removes `EXPORT_EXCEPTION_HANDLING_HELPERS`. (You can use it but it won't do anything additionally)
This effectively removes `EXPORT_EXCEPTION_HANDLING_HELPERS` setting. This marks the setting as deprecated not to crash users' builds right away in case they are using it. Even though it still exists as a deprecated setting, setting it to true will not change anything. It used to export `getExceptionMessage` and a few more functions (`in/decrementexceptionRefCount`), but after emscripten-core#26493, `getExceptionMessage` is exported anyway when exceptions are used and either `-sASSERTIONS` or `-sEXCEPTION_STACK_TRACES` is set, which are set by default at `-O0`. For Wasm EH, the dependency is automatically detected. For Emscripten EH, we had to add `getExceptionMessage` to deps of `__cxa_throw`. This adds `in/decrementexceptionRefCount` to deps of `__cxa_throw` (for Emscripten EH) and `__throw_exception_with_stack_trace` (for Wasm EH) and removes `EXPORT_EXCEPTION_HANDLING_HELPERS`. (You can use it but it won't do anything additionally)
This effectively removes `EXPORT_EXCEPTION_HANDLING_HELPERS` setting. This marks the setting as deprecated not to crash users' builds right away in case they are using it. Even though it still exists as a deprecated setting, setting it to true will not change anything. It used to export `getExceptionMessage` and a few more functions (`in/decrementexceptionRefCount`), but after #26493, `getExceptionMessage` is exported anyway when exceptions are used and either `-sASSERTIONS` or `-sEXCEPTION_STACK_TRACES` is set, which are set by default at `-O0`. For Wasm EH, the dependency is automatically detected. For Emscripten EH, we had to add `getExceptionMessage` to deps of `__cxa_throw`. This adds `in/decrementexceptionRefCount` to deps of `__cxa_throw` (for Emscripten EH) and `__throw_exception_with_stack_trace` (for Wasm EH) and removes `EXPORT_EXCEPTION_HANDLING_HELPERS`. (You can use it but it won't do anything additionally)
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.
We used to enable
EXPORT_EXCEPTION_HANDLING_HELPERS, which exportsgetExceptionMessageandin/decrementExceptionRefcount, wheneverEXCEPTION_STACK_TRACESwas true. AndEXCEPTION_STACK_TRACESis true wheneverASSERTIONSis 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_HELPERSlink. Without this link, we can still see stack traces with exception messages with no problem, because__cxa_throw->__throw_exception_with_stack_trace->getExceptionMessagedependencies:emscripten/system/lib/libcxxabi/src/cxa_exception.cpp
Lines 302 to 304 in 6ad2f5e
emscripten/src/lib/libexceptions.js
Line 311 in 6ad2f5e
If we do 1, Emscripten EH's
getExceptionMessagedoes not work, because unlike Wasm EH, itsgetExceptionMessagedependency is withinruntime_exceptions.js, where we can't attach__deps, so we can't track it:emscripten/src/runtime_exceptions.js
Line 20 in 6ad2f5e
getExceptionMessageas a dependency oflibexception.js's__cxa_throwdirectly, whenEXCEPTION_STACK_TRACESis enabled.This removes several functions from
REQUIRED_EXPORTSwhen Emscripten EH is used. Previously, the comment said, in LTO mode,_cxa_find_matching_catch_*->__cxa_can_catchdependency was not tracked. But now, in Emscripten EH, we preemptively add several__cxa_find_matching_catch_ns, and it depends onfindMatchingCatch, which depends on__cxa_end_catch:emscripten/src/lib/libexceptions.js
Lines 371 to 405 in 7840305
emscripten/src/lib/libexceptions.js
Line 217 in 7840305
__cxa_end_catchanymore.Also, other required exports (
__cxa_in/decrement_exception_countand__cxa_free_exceptions) are dependencies that can be naturally referred within libc++abi. I think these were added here in Move parts of emscripten exception handling to native code. NFC #16627 when we were usingdeps_info.py, which we don't use anymore.After this commit, when you compile an empty
int main { return 0; }with-O0and-fexceptions/-fwasm-exceptions, the size decreases to:-fexceptions: 113212 -> 1168-fwasm-exceptions: 109177 -> 1106