Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++] [libc++abi] Initialize exception directly in make_exception_ptr if __cxa_init_primary_exception is available in ABI-library #65534

Merged
merged 82 commits into from
Jan 22, 2024

Conversation

itrofimow
Copy link
Contributor

@itrofimow itrofimow commented Sep 6, 2023

This patch implements an ABI-extension function __cxa_init_primary_exception in libcxxabi, which is already present as an extension in both libsupc++ and libcxxrt, and makes use of this function in std::make_exception_ptr: now instead of going through full throw + catch cycle we are able to initialize an exception directly, thus making std::make_exception_ptr around x30 times faster.

@github-actions github-actions bot added libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. libc++-abi labels Sep 6, 2023
@itrofimow itrofimow force-pushed the dont_throw_in_make_exception_ptr branch from 969531a to 786f1f4 Compare September 25, 2023 13:34
@EugeneZelenko EugeneZelenko added libc++abi libc++abi C++ Runtime Library. Not libc++. and removed libc++-abi labels Sep 25, 2023
@github-actions
Copy link

github-actions bot commented Sep 27, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@itrofimow itrofimow force-pushed the dont_throw_in_make_exception_ptr branch 5 times, most recently from 3373241 to ec0229c Compare October 10, 2023 00:14
@itrofimow
Copy link
Contributor Author

I had to split typeinfo header to break the exception -> typeinfo -> exception circular dependency, hence a sizeable +/-, other than that the PR is basically a couple dozen LoC.

The build is failing mostly due to missing changes in abi-lists for various architectures, i'm planning to fix that once/if the general idea is approved.

One question left is the way to determine whether the new __cxa_init_primary_exception function is available: right now i'm relying on LIBCXX_BUILDING_LIBCXXABI, but i think it's not enough.
Should i add a new define into libcxxabi, or check for __cxa_init_primary_exception presence at runtime via dlsym?
Personally i'm leaning towards dlsym, since it would not only allow linking with other versions of libcxxabi, but potentially implementing the same optimization when linked against libcxxrt, see libcxxrt/libcxxrt#23.

@philnik777 @EricWF Could you please have a look?

@itrofimow itrofimow marked this pull request as ready for review October 11, 2023 14:35
@itrofimow itrofimow requested review from a team as code owners October 11, 2023 14:35
Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at the actual changes closely yet, but the amount of extra code seems reasonable for a significant speedup.

libcxx/include/__exception/exception_ptr.h Outdated Show resolved Hide resolved
libcxx/include/__exception/exception_ptr.h Outdated Show resolved Hide resolved
@@ -51,11 +69,24 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr {
template <class _Ep>
_LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep __e) _NOEXCEPT {
# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
# if defined(LIBCXX_BUILDING_LIBCXXABI)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be guarded by an availability macro instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a macro or a runtime availability check instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added support for libcxxrt as well and made it a runtime check

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ifdef became a bit too big to copy-paste it, so i moved it into __config

libcxx/include/typeinfo Outdated Show resolved Hide resolved
libcxxabi/include/cxxabi.h Show resolved Hide resolved
@itrofimow
Copy link
Contributor Author

itrofimow commented Oct 15, 2023

Benchmarks to back the performance claims up:

itrofimow@itrofimow-nix:~/work/llvm-project/ex_ptr_bench$ taskset -c 8-15 ~/work/gbench/tools/compare.py benchmarks ./main_libcxxabi ./patch_libcxxabi 
RUNNING: ./main_libcxxabi --benchmark_out=/tmp/tmpi96tlrpa
2023-10-15T20:57:14+03:00
Running ./main_libcxxabi
Run on (16 X 1280.96 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x8)
  L1 Instruction 32 KiB (x8)
  L2 Unified 1280 KiB (x8)
  L3 Unified 24576 KiB (x1)
Load Average: 0.36, 1.30, 1.61
--------------------------------------------------------------------------
Benchmark                                Time             CPU   Iterations
--------------------------------------------------------------------------
bm_make_exception_ptr/threads:1        745 ns          745 ns       950011
bm_make_exception_ptr/threads:2        816 ns         1633 ns       455610
bm_make_exception_ptr/threads:4        863 ns         3208 ns       227044
bm_make_exception_ptr/threads:8       1120 ns         8064 ns        87896
RUNNING: ./patch_libcxxabi --benchmark_out=/tmp/tmpvd2nklsl
2023-10-15T20:57:16+03:00
Running ./patch_libcxxabi
Run on (16 X 1259.78 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x8)
  L1 Instruction 32 KiB (x8)
  L2 Unified 1280 KiB (x8)
  L3 Unified 24576 KiB (x1)
Load Average: 0.36, 1.30, 1.61
--------------------------------------------------------------------------
Benchmark                                Time             CPU   Iterations
--------------------------------------------------------------------------
bm_make_exception_ptr/threads:1       25.6 ns         25.6 ns     27321613
bm_make_exception_ptr/threads:2       12.7 ns         25.4 ns     27881074
bm_make_exception_ptr/threads:4       6.84 ns         27.4 ns     27500684
bm_make_exception_ptr/threads:8       3.56 ns         28.5 ns     25787520
Comparing ./main_libcxxabi to ./patch_libcxxabi
Benchmark                                         Time             CPU      Time Old      Time New       CPU Old       CPU New
------------------------------------------------------------------------------------------------------------------------------
bm_make_exception_ptr/threads:1                -0.9656         -0.9656           745            26           745            26
bm_make_exception_ptr/threads:2                -0.9844         -0.9844           816            13          1633            25
bm_make_exception_ptr/threads:4                -0.9921         -0.9915           863             7          3208            27
bm_make_exception_ptr/threads:8                -0.9968         -0.9965          1120             4          8064            28
OVERALL_GEOMEAN                                -0.9892         -0.9887             0             0             0             0

It can be seen that with the patch not only the performance is x30(!) in single-threaded case, but it also scales linearly with the number of threads (CPU / Time == number of threads), when on current trunk it's not the case: i'm using not the most recent gcc's unwinder, so contention on the lock in dl_iterate_phdr takes place.

The effect is the same with libcxxrt, although it seems to be a bit slower overall.

I've addressed review comments and the build is green, so re-requesting the review.

@itrofimow itrofimow changed the title [libcxx] [libcxxabi] Dont throw in make exception ptr if __cxa_init_primary_exception is available [libc++] [libc++abi] Initialize exception directly in make_exception_ptr if __cxa_init_primary_exception is available in ABI-library Oct 15, 2023
Copy link
Member

@jyknight jyknight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, seems like a good idea; I haven't reviewed the whole thing, but here's some comments.

libcxx/include/__exception/exception_ptr.h Outdated Show resolved Hide resolved
// and the caller knows how to handle this (it fallbacks to throw + catch).
using CxaInitPrimaryExceptionPrototype = void *(*)(void *, type_info *, void(*)(void *));
static CxaInitPrimaryExceptionPrototype cxa_init_primary_exception_fn = reinterpret_cast<CxaInitPrimaryExceptionPrototype>(
dlsym(RTLD_DEFAULT, "__cxa_init_primary_exception"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to use a weak reference to the __cxa_init_primary_exception instead of dlsym.

That way, the loader takes care of the lookup, and if you are building libc++ and libc++abi together (which is the common case), there's no extra overhead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently that doesn't seem to work with Apple.
I'm fine with that, since i'm only interested in linux platform, and for now i ifdefed-out Apple

libcxxabi/include/cxxabi.h Show resolved Hide resolved
# if defined(LIBCXXRT) || defined(LIBCXX_BUILDING_LIBCXXABI)
using _Ep2 = __decay_t<_Ep>;
void* __ex = exception_ptr::__init_native_exception(
sizeof(_Ep), const_cast<std::type_info*>(&typeid(_Ep)), exception_ptr::__dest_thunk<_Ep2>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't use typeid with -fno-rtti, but exceptions can be used with -fno-rtti so the previous impl would work. So this code should all be in a !_LIBCPP_HAS_NO_RTTI ifdef.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't use typeid with -fno-rtti, but exceptions can be used with -fno-rtti so the previous impl would work. So this code should all be in a !_LIBCPP_HAS_NO_RTTI ifdef.

Hmm... (at least on Itanium ABI?) when exceptions need to be thrown, type_info objects are generated and passed on throwing even if -fno-rtti is used.

Is there any intrinsic to obtain RTTI for EH when -fno-rtti is used?

@itrofimow itrofimow force-pushed the dont_throw_in_make_exception_ptr branch 3 times, most recently from 2c6c34d to c74746d Compare October 24, 2023 01:16
Copy link
Contributor Author

@itrofimow itrofimow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! I've addressed/answered the inline comments and manually checked that libcxx builds + works with libcxxabi/libsupc++/libcxxrt (should there be a CI-build with such combinations?), so re-requesting the review.

@philnik777 @jyknight @ldionne

libcxx/include/new Show resolved Hide resolved
exception_ptr exception_ptr::__from_native_exception_pointer(void* __e) noexcept
{
exception_ptr ptr{};
new (reinterpret_cast<void*>(&ptr)) __exception_ptr::exception_ptr(__e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has got to be an aliasing violation that the compiler can optimize on?

The types have two different sizes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do they?

exception_ptr::exception_ptr(const exception_ptr& other) noexcept
: __ptr_(other.__ptr_)
{
new (reinterpret_cast<void*>(this)) __exception_ptr::exception_ptr(
reinterpret_cast<const __exception_ptr::exception_ptr&>(other));
}

My understanding is that this is a hackery of course, but the hackery that should work:

// we have little choice but to hijack std::__exception_ptr::exception_ptr's
// (which fortunately has the same layout as our std::exception_ptr) copy
// constructor, assignment operator and destructor (which are part of its
// stable ABI), and its rethrow_exception(std::__exception_ptr::exception_ptr)

@@ -25,6 +36,9 @@ struct exception_ptr
{
void* __ptr_;

# if defined(_LIBCPP_EXCEPTION_PTR_DIRECT_INIT)
exception_ptr(void*) noexcept;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit please.

libcxx/include/__exception/exception_ptr.h Show resolved Hide resolved
} catch (...) {
return current_exception();
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have an unreachable annotation at the bottom of this block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it, but could you please explain to me why is it needed?

} catch (...) {
exception_ptr::__free_native_exception(__ex);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we know that we don't also have to destroy the constructed exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only get here if the exception constructor threw, so there's no object to destroy. Am i missing something?

# if defined(_LIBCPP_EXCEPTION_PTR_DIRECT_INIT)
using _Ep2 = __decay_t<_Ep>;
void* __ex = exception_ptr::__init_native_exception(
sizeof(_Ep), const_cast<std::type_info*>(&typeid(_Ep)), exception_ptr::__dest_thunk<_Ep2>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code allocates for _Ep here but then constructs _Ep2. Similarly, it uses the typeinfo for _Ep.

We're sure this is correct?

@@ -1048,6 +1048,10 @@ __sanitizer_verify_double_ended_contiguous_container(const void*, const void*, c
# define _LIBCPP_HAS_NO_RTTI
# endif

# if !defined(_LIBCPP_HAS_NO_EXCEPTIONS) && !defined(_LIBCPP_HAS_NO_RTTI) && !defined(__APPLE__) && !defined(_WIN32)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this disabled on Apple and on Windows? It seems to me like this should be an availability macro instead so that all platforms can take advantage of it as long as the deployment target is recent enough to support this new addition. I think this is what @philnik777 was referring to in another comment.

Copy link
Contributor Author

@itrofimow itrofimow Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "availability macro": is that https://clang.llvm.org/docs/AttributeReference.html#availability?
Anyway, i'm failing to understand how that could eliminate the need to have a runtime check for the __cxa_init_primary_exception presence -- say, if i build against 18.0 libcxxabi and then LD_PRELOAD 17.0 version, which should be a supported case, right?


To answer your question directly:
Windows: disabled due to mingw requiring this

#define _LIBCXXABI_DTOR_FUNC __thiscall
to be specified for exception destructor, and this attribute now has to appear in libc++ at the call site (where weak declaration of the __cxa_init_primary_exception is present and the exception destructor is defined). I didn't want to copy-paste this macro in libc++, but definitely doable, should i implement it?

Apple: back-deployment-macosx tests fail (https://buildkite.com/llvm-project/libcxx-ci/builds/30969#018b50e0-c82b-42e0-bceb-b74d5808ffad, for example) with dyld[47810]: missing symbol called error. I have very limited knowledge about how linking is performed on Apple, but my quick googling tells that it's much different from what ld does, and weak attributes don't work without some compiler flags specified. I have no clue how this should be fixed and it also takes forever for CI to reach this check, so any help would be appreciated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a mingw support

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look at how the availability macros in __availability work, basically we use the --target <arch>-<vendor>-<platform><version> to drive this. The idea is that you tell the compiler what's the oldest version of the platform you are targeting, and based on that we make the function available or not. If you then decide to run your program on an older platform, you effectively lied to the compiler and in practice you'll get a load-time error.

@ldionne ldionne self-assigned this Jan 16, 2024
@ldionne ldionne added this to the LLVM 18.0.X Release milestone Jan 16, 2024
@itrofimow
Copy link
Contributor Author

I had to spend some time bisecting very cryptic error: undefined symbol: _Unwind_Resume when building libc++ with libcxxrt/libsupc++ to this PR #77687, so i've adjusted the docs to explicitly disable flipped default.

Building with libcxxrt also requires DLIBCXX_ENABLE_NEW_DELETE_DEFINITIONS to be set, otherwise operator new/operator delete are undefined.

Also, addressing the last outstanding comment about undef-ing the availability macro i realized that _LIBCPP_AVAILABILITY_INIT_PRIMARY_EXCEPTION isn't used anywhere and I'm not really sure to what it should be applied.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM!

@itrofimow
Copy link
Contributor Author

The tests failing are ARMv7-picolib tests, and they fail with

Unexpectedly Passed Tests (2):
  armv7m-picolibc-libc++.cfg.in :: libcxx/selftest/modules/std-module.sh.cpp
  armv7m-picolibc-libc++.cfg.in :: std/modules/std.pass.cpp

which i don't think is related to this PR changes

@ldionne
Copy link
Member

ldionne commented Jan 22, 2024

Indeed, those CI failures are unrelated and should be fixed on main. Merging this now.

@ldionne ldionne merged commit 51e91b6 into llvm:main Jan 22, 2024
43 of 44 checks passed
@itrofimow
Copy link
Contributor Author

Indeed, those CI failures are unrelated and should be fixed on main. Merging this now.

Thank you for all the time you've spent helping me with this PR.
Please don't hesitate to ping me here or on discord if something goes wrong with these changes.

@nico
Copy link
Contributor

nico commented Jan 22, 2024

Hello, my (unsupported, non-standard, likely-not-correctly-set-up) bot started failing to build after this: http://45.33.8.238/linux/128697/step_4.txt

Looking through the code a bit, I think libcxx/src/exception.cpp includes support/runtime/exception_pointer_unimplemented.ipp if we're on non-MS and neither of _LIBCPPABI_VERSION, LIBCXXRT, __GLIBCXX__ are defined. Before this change, things linked fine in that config. But since this adds a call to __from_native_exception_pointer() on non-MS unconditionally but doesn't add an impl of that to exception_pointer_unimplemented.ipp, that config now fails to link. Is that an intentional change?

(For my bot, it's probably incorrect that none of these macros are defined! But exception.cpp kind of looks like that case is supposed to work, so I thought I'd raise this here.)

nico added a commit that referenced this pull request Jan 22, 2024
See here for context:
#65534 (comment)
@itrofimow
Copy link
Contributor Author

Hi @nico! No, that is absolutely not intentional, i've overlooked that, sorry.
Could you please check if your bot builds with this patch applied? https://github.com/llvm/llvm-project/pull/79040/files

@nico
Copy link
Contributor

nico commented Jan 23, 2024

I checked out 309dcc1^ and verified that I could repro the link error. Then I patched in #79040 and verified that it went away. So that PR helps, thanks!

ldionne pushed a commit that referenced this pull request Jan 23, 2024
…n_ptr (#79040)

This patch fixes linkage for platforms that don't implement
std::exception_ptr, as such setup was overlooked in #65534.
@DimitryAndric
Copy link
Collaborator

FWIW this caused an incompatibility with recent libcxxrt (after libcxxrt/libcxxrt@03c83f5), since its cxxabi.h declares __cxa_allocate_exception, __cxa_free_exception and __cxa_init_primary_exception without throw() specifiers.

I submitted libcxxrt/libcxxrt#25, since I am not sure about the official signature of these functions, if there is any.

@ldionne
Copy link
Member

ldionne commented Jan 25, 2024

@DimitryAndric Thanks, please keep us in the loop. If we need to make a fix on our side, @itrofimow we'll want to cherry-pick it to LLVM 18.

@dankm
Copy link
Contributor

dankm commented Jan 26, 2024

@ldionne it looks like the consensus is that GCC and LLVM are correct and that we're fixing libcxxrt. The relevant pull request is libcxxrt/libcxxrt#27.

@ldionne
Copy link
Member

ldionne commented Jan 26, 2024

Thanks for the heads up!

aheejin added a commit to aheejin/emscripten that referenced this pull request Mar 28, 2024
llvm/llvm-project#65534 introduces
`__cxa_init_primary_exception` and uses this in libcxx. Like several
other methods like the below in `cxa_exception.cpp`,
https://github.com/emscripten-core/emscripten/blob/fbdd9249e939660cb0d20f00d6bc1897c2f3905e/system/lib/libcxxabi/src/cxa_exception.cpp#L264-L269
this new function takes a pointer to a destructor, and in Wasm
destructor returns a `this` pointer, which requires
`ifdef __USING_WASM_EXCEPTION__` on its signature.

And that it is also used in libcxx means we need to guard some code in
libcxx with `ifdef __USING_WASM_EXCEPTION__` for the signature
difference, and we need to build libcxx with
`-D__USING_WASM_EXCEPTIONS__` when Wasm EH is used.
aheejin added a commit to aheejin/emscripten that referenced this pull request Mar 29, 2024
llvm/llvm-project#65534 added
`__cxa_init_primary_exception` function and use it in both in
libcxxabi's `__cxa_throw` and also directly in libcxx. Because we create
exceptions in JS, we need a counterpart in JS exception library too.
aheejin added a commit to emscripten-core/emscripten that referenced this pull request Apr 3, 2024
This updates libcxx and libcxxabi to LLVM 18.1.2. Due to
llvm/llvm-project#65534 we need to update both
at the same time.

Things to note:

- Disable hardening mode:
  LLVM 18.1.2 adds support for "Hardening Modes" to libcxx
  (https://libcxx.llvm.org/Hardening.html). There are four modes: none,
  fast, extensive and debug. Different hardening modes make different
  trade-offs between the amount of checking and runtime performance. We
  for now disable it (i.e., set it to 'none') so that we don't have any
  effects on the performance. We can consider enabling it when we ever
  get to enable the debug version of libcxx.

- Disable C++20 time zone support:
  LLVM 18.1.2 adds C++20 time zone support
  (https://libcxx.llvm.org/DesignDocs/TimeZone.html) to libcxx, which
  requires access IANA Time Zone Database. Currently it seems it only
  supports Linux:
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/src/tz.cpp#L45-L49
  So this excludes the two source files from build (which is done via
  `CMakeLists.txt` in the upstream LLVM) and sets
  `_LIBCPP_HAS_NO_TIME_ZONE_DATABASE` macro in `__config_site`. In
  future maybe we can consider implementing this in JS.

- `__cxa_init_primary_exception` support:
  llvm/llvm-project#65534 introduces
  `__cxa_init_primary_exception` and uses this in libcxx. Like several
  other methods like the below in `cxa_exception.cpp`,
  https://github.com/emscripten-core/emscripten/blob/fbdd9249e939660cb0d20f00d6bc1897c2f3905e/system/lib/libcxxabi/src/cxa_exception.cpp#L264-L269
  this new function takes a pointer to a destructor, and in Wasm
  destructor returns a `this` pointer, which requires `ifdef
  __USING_WASM_EXCEPTION__` on its signature. And that it is also used
  in libcxx means we need to guard some code in libcxx with `ifdef
  __USING_WASM_EXCEPTION__` for the signature difference, and we need to
  build libcxx with `-D__USING_WASM_EXCEPTIONS__` when Wasm EH is used.
  Also we add Emscripte EH's counterpart in
  `cxa_exception_emscripten.cpp` too.

- This version fixes long-running misbehaviors of `operator new`
  llvm/llvm-project#69498 seems to fix some
  long-running misbhaviors of `operator new`, which in emscripten we
  tried to fix in #11079 and #20154. So while resolving the conflicts, I
  effectively reverted #11079 and #20154 in
  `libcxx/src/stdlib_new_delete.cpp` and `libcxx/src/new.cpp`.

- Add default `__assertion_handler` to `libcxx/include`:
  llvm/llvm-project#77883 adds a way for vendors
  to override how we handle assertions. If a vendor doesn't want to
  override it, CMake will copy the provided [default template assertion
  handler
  file](https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/vendor/llvm/default_assertion_handler.in)
  to libcxx's generated include dir, which defaults to
  `SYSROOT/include/c++/v1`:
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/CMakeLists.txt#L72-L73
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/CMakeLists.txt#L439
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/include/CMakeLists.txt#L1024
  We don't use CMake, so this renames the provided
  `vendor/llvm/default_assertion_handler.in` to `__assert_handler` in
  our `libcxx/include` directory, which will be copied to
  `SYSROOT/include/c++/v1`.

- Disable `__libcpp_verbose_abort directly` in code:
  In #20707 we decided to disable `__libcpp_verbose_abort` not to incur
  the code size increase that brings (it brings in `vfprintf` and its
  family). We disabled it in adding
  `#define _LIBCPP_AVAILABILITY_HAS_NO_VERBOSE_ABORT` in
  `__config_site`. But that `_NO_` macros are gone in LLVM 18.1.2, and
  changing it to `#define _LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT 0` does
  not work because it is overridden by this line, unless we provide our
  own vendor annotations:
  https://github.com/llvm/llvm-project/blob/38f5596feda3276a8aa64fc14e074334017088ca/libcxx/include/__availability#L138
  I asked about this in
  llvm/llvm-project#87012 and
  llvm/llvm-project#71002 (comment)
  (and more comments below)
  but didn't get an actionable answer yet. So this disables
  `__libcpp_verbose_abort` in the code directly for now, hopefully
  temporarily.

Each fix is described in its own commit, except for the fixes required
to resolve conflicts when merging our changes, which I wasn't able to
commit separately.

Fixes #21603.
impact-maker pushed a commit to impact-maker/emscripten that referenced this pull request Apr 11, 2024
This updates libcxx and libcxxabi to LLVM 18.1.2. Due to
llvm/llvm-project#65534 we need to update both
at the same time.

Things to note:

- Disable hardening mode:
  LLVM 18.1.2 adds support for "Hardening Modes" to libcxx
  (https://libcxx.llvm.org/Hardening.html). There are four modes: none,
  fast, extensive and debug. Different hardening modes make different
  trade-offs between the amount of checking and runtime performance. We
  for now disable it (i.e., set it to 'none') so that we don't have any
  effects on the performance. We can consider enabling it when we ever
  get to enable the debug version of libcxx.

- Disable C++20 time zone support:
  LLVM 18.1.2 adds C++20 time zone support
  (https://libcxx.llvm.org/DesignDocs/TimeZone.html) to libcxx, which
  requires access IANA Time Zone Database. Currently it seems it only
  supports Linux:
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/src/tz.cpp#L45-L49
  So this excludes the two source files from build (which is done via
  `CMakeLists.txt` in the upstream LLVM) and sets
  `_LIBCPP_HAS_NO_TIME_ZONE_DATABASE` macro in `__config_site`. In
  future maybe we can consider implementing this in JS.

- `__cxa_init_primary_exception` support:
  llvm/llvm-project#65534 introduces
  `__cxa_init_primary_exception` and uses this in libcxx. Like several
  other methods like the below in `cxa_exception.cpp`,
  https://github.com/emscripten-core/emscripten/blob/fbdd9249e939660cb0d20f00d6bc1897c2f3905e/system/lib/libcxxabi/src/cxa_exception.cpp#L264-L269
  this new function takes a pointer to a destructor, and in Wasm
  destructor returns a `this` pointer, which requires `ifdef
  __USING_WASM_EXCEPTION__` on its signature. And that it is also used
  in libcxx means we need to guard some code in libcxx with `ifdef
  __USING_WASM_EXCEPTION__` for the signature difference, and we need to
  build libcxx with `-D__USING_WASM_EXCEPTIONS__` when Wasm EH is used.
  Also we add Emscripte EH's counterpart in
  `cxa_exception_emscripten.cpp` too.

- This version fixes long-running misbehaviors of `operator new`
  llvm/llvm-project#69498 seems to fix some
  long-running misbhaviors of `operator new`, which in emscripten we
  tried to fix in emscripten-core#11079 and emscripten-core#20154. So while resolving the conflicts, I
  effectively reverted emscripten-core#11079 and emscripten-core#20154 in
  `libcxx/src/stdlib_new_delete.cpp` and `libcxx/src/new.cpp`.

- Add default `__assertion_handler` to `libcxx/include`:
  llvm/llvm-project#77883 adds a way for vendors
  to override how we handle assertions. If a vendor doesn't want to
  override it, CMake will copy the provided [default template assertion
  handler
  file](https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/vendor/llvm/default_assertion_handler.in)
  to libcxx's generated include dir, which defaults to
  `SYSROOT/include/c++/v1`:
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/CMakeLists.txt#L72-L73
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/CMakeLists.txt#L439
  https://github.com/llvm/llvm-project/blob/26a1d6601d727a96f4301d0d8647b5a42760ae0c/libcxx/include/CMakeLists.txt#L1024
  We don't use CMake, so this renames the provided
  `vendor/llvm/default_assertion_handler.in` to `__assert_handler` in
  our `libcxx/include` directory, which will be copied to
  `SYSROOT/include/c++/v1`.

- Disable `__libcpp_verbose_abort directly` in code:
  In emscripten-core#20707 we decided to disable `__libcpp_verbose_abort` not to incur
  the code size increase that brings (it brings in `vfprintf` and its
  family). We disabled it in adding
  `#define _LIBCPP_AVAILABILITY_HAS_NO_VERBOSE_ABORT` in
  `__config_site`. But that `_NO_` macros are gone in LLVM 18.1.2, and
  changing it to `#define _LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT 0` does
  not work because it is overridden by this line, unless we provide our
  own vendor annotations:
  https://github.com/llvm/llvm-project/blob/38f5596feda3276a8aa64fc14e074334017088ca/libcxx/include/__availability#L138
  I asked about this in
  llvm/llvm-project#87012 and
  llvm/llvm-project#71002 (comment)
  (and more comments below)
  but didn't get an actionable answer yet. So this disables
  `__libcpp_verbose_abort` in the code directly for now, hopefully
  temporarily.

Each fix is described in its own commit, except for the fixes required
to resolve conflicts when merging our changes, which I wasn't able to
commit separately.

Fixes emscripten-core#21603.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++abi libc++abi C++ Runtime Library. Not libc++. libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants