From 8b33204f371cc392fd3b72270bd2bd4ebec625dd Mon Sep 17 00:00:00 2001 From: Trung Nguyen <57174311+trungnt2910@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:45:10 +0700 Subject: [PATCH] Add LLVM libcxxabi (#1063) * third_party: Add libcxxabi Added libcxxabi from LLVM 17.0.6 The library implements the Itanium C++ exception handling ABI. * third_party/libcxxabi: Enable __cxa_thread_atexit Enable `__cxa_thread_atexit` from libcxxabi. `__cxa_thread_atexit_impl` is still implemented by the cosmo libc. The original `__cxa_thread_atexit` has been removed. * third_party/libcxx: Build with exceptions Build libcxx with exceptions enabled. - Removed `_LIBCPP_NO_EXCEPTIONS` from `__config`. - Switched the exception implementation to `libcxxabi`. These two files are taken from the same `libcxx` version as mentioned in `README.cosmo`. - Removed `new_handler_fallback` in favor of `libcxxabi` implementation. - Enable `-fexceptions` and `-frtti` for `libcxx`. - Removed `THIRD_PARTY_LIBCXX` dependency from `libcxxabi` and `libunwind`. These libraries do not use any runtime `libcxx` functions, just headers. * libc: Remove remaining redundant cxa functions - `__cxa_pure_virtual` in `libcxxabi` is also a stub similar to the existing one. - `__cxa_guard_*` from `libcxxabi` is used instead of the ones from Android. Now there should be no more duplicate implementations. `__cxa_thread_atexit_impl`, `__cxa_atexit`, and related supporting functions, are still left to other libraries as in `libcxxabi`. `libcxxabi` is also now added to `cosmopolitan.a` to make up for the removed functions. Affected in-tree libraries (`third_party/double-conversion`) have been updated. --- Makefile | 6 +- libc/intrin/__cxa_pure_virtual.c | 28 - libc/runtime/cxaguard.c | 115 - libc/thread/__cxa_thread_atexit.c | 23 - test/libc/thread/BUILD.mk | 1 + third_party/BUILD.mk | 1 + third_party/double-conversion/BUILD.mk | 5 +- third_party/libcxx/BUILD.mk | 14 +- third_party/libcxx/__config | 1 - third_party/libcxx/exception.cc | 11 +- third_party/libcxx/exception_fallback.hh | 111 - ...ler_fallback.hh => exception_libcxxabi.hh} | 21 +- .../libcxx/exception_pointer_cxxabi.hh | 73 + .../libcxx/exception_pointer_unimplemented.hh | 68 - third_party/libcxx/new.cc | 2 +- third_party/libcxxabi/BUILD.mk | 112 + third_party/libcxxabi/CREDITS.TXT | 71 + third_party/libcxxabi/LICENSE.TXT | 311 + third_party/libcxxabi/README.cosmo | 18 + third_party/libcxxabi/abort_message.cc | 79 + third_party/libcxxabi/abort_message.h | 17 + third_party/libcxxabi/aix_state_tab_eh.inc | 745 +++ third_party/libcxxabi/cxa_aux_runtime.cc | 44 + third_party/libcxxabi/cxa_default_handlers.cc | 141 + third_party/libcxxabi/cxa_demangle.cc | 409 ++ third_party/libcxxabi/cxa_exception.cc | 776 +++ third_party/libcxxabi/cxa_exception.h | 164 + .../libcxxabi/cxa_exception_storage.cc | 103 + third_party/libcxxabi/cxa_guard.cc | 53 + third_party/libcxxabi/cxa_guard_impl.h | 674 ++ third_party/libcxxabi/cxa_handlers.cc | 101 + third_party/libcxxabi/cxa_handlers.h | 55 + third_party/libcxxabi/cxa_personality.cc | 1315 ++++ third_party/libcxxabi/cxa_thread_atexit.cc | 145 + third_party/libcxxabi/cxa_vector.cc | 421 ++ third_party/libcxxabi/cxa_virtual.cc | 24 + .../libcxxabi/demangle/DemangleConfig.h | 105 + .../libcxxabi/demangle/ItaniumDemangle.h | 5520 +++++++++++++++++ .../libcxxabi/demangle/ItaniumNodes.def | 95 + .../libcxxabi/demangle/StringViewExtras.h | 38 + third_party/libcxxabi/demangle/Utility.h | 205 + third_party/libcxxabi/fallback_malloc.cc | 303 + third_party/libcxxabi/fallback_malloc.h | 28 + .../libcxxabi/include/__cxxabi_config.h | 106 + third_party/libcxxabi/include/cxxabi.h | 177 + .../libcxx/include/__memory/aligned_alloc.h | 66 + third_party/libcxxabi/private_typeinfo.cc | 1388 +++++ third_party/libcxxabi/private_typeinfo.h | 251 + third_party/libcxxabi/stdlib_exception.cc | 70 + third_party/libcxxabi/stdlib_new_delete.cc | 266 + third_party/libcxxabi/stdlib_stdexcept.cc | 47 + third_party/libcxxabi/stdlib_typeinfo.cc | 52 + third_party/libunwind/BUILD.mk | 3 +- 53 files changed, 14606 insertions(+), 372 deletions(-) delete mode 100644 libc/intrin/__cxa_pure_virtual.c delete mode 100644 libc/runtime/cxaguard.c delete mode 100644 libc/thread/__cxa_thread_atexit.c delete mode 100644 third_party/libcxx/exception_fallback.hh rename third_party/libcxx/{new_handler_fallback.hh => exception_libcxxabi.hh} (52%) create mode 100644 third_party/libcxx/exception_pointer_cxxabi.hh delete mode 100644 third_party/libcxx/exception_pointer_unimplemented.hh create mode 100644 third_party/libcxxabi/BUILD.mk create mode 100644 third_party/libcxxabi/CREDITS.TXT create mode 100644 third_party/libcxxabi/LICENSE.TXT create mode 100644 third_party/libcxxabi/README.cosmo create mode 100644 third_party/libcxxabi/abort_message.cc create mode 100644 third_party/libcxxabi/abort_message.h create mode 100644 third_party/libcxxabi/aix_state_tab_eh.inc create mode 100644 third_party/libcxxabi/cxa_aux_runtime.cc create mode 100644 third_party/libcxxabi/cxa_default_handlers.cc create mode 100644 third_party/libcxxabi/cxa_demangle.cc create mode 100644 third_party/libcxxabi/cxa_exception.cc create mode 100644 third_party/libcxxabi/cxa_exception.h create mode 100644 third_party/libcxxabi/cxa_exception_storage.cc create mode 100644 third_party/libcxxabi/cxa_guard.cc create mode 100644 third_party/libcxxabi/cxa_guard_impl.h create mode 100644 third_party/libcxxabi/cxa_handlers.cc create mode 100644 third_party/libcxxabi/cxa_handlers.h create mode 100644 third_party/libcxxabi/cxa_personality.cc create mode 100644 third_party/libcxxabi/cxa_thread_atexit.cc create mode 100644 third_party/libcxxabi/cxa_vector.cc create mode 100644 third_party/libcxxabi/cxa_virtual.cc create mode 100644 third_party/libcxxabi/demangle/DemangleConfig.h create mode 100644 third_party/libcxxabi/demangle/ItaniumDemangle.h create mode 100644 third_party/libcxxabi/demangle/ItaniumNodes.def create mode 100644 third_party/libcxxabi/demangle/StringViewExtras.h create mode 100644 third_party/libcxxabi/demangle/Utility.h create mode 100644 third_party/libcxxabi/fallback_malloc.cc create mode 100644 third_party/libcxxabi/fallback_malloc.h create mode 100644 third_party/libcxxabi/include/__cxxabi_config.h create mode 100644 third_party/libcxxabi/include/cxxabi.h create mode 100644 third_party/libcxxabi/libcxx/include/__memory/aligned_alloc.h create mode 100644 third_party/libcxxabi/private_typeinfo.cc create mode 100644 third_party/libcxxabi/private_typeinfo.h create mode 100644 third_party/libcxxabi/stdlib_exception.cc create mode 100644 third_party/libcxxabi/stdlib_new_delete.cc create mode 100644 third_party/libcxxabi/stdlib_stdexcept.cc create mode 100644 third_party/libcxxabi/stdlib_typeinfo.cc diff --git a/Makefile b/Makefile index dd6eebf26..ea74d8296 100644 --- a/Makefile +++ b/Makefile @@ -249,7 +249,6 @@ include libc/calls/BUILD.mk #─┐ include libc/irq/BUILD.mk # ├──SYSTEMS RUNTIME include third_party/nsync/BUILD.mk # │ You can issue system calls include libc/runtime/BUILD.mk # │ -include third_party/double-conversion/BUILD.mk # │ include libc/crt/BUILD.mk # │ include third_party/dlmalloc/BUILD.mk #─┘ include libc/mem/BUILD.mk #─┐ @@ -284,7 +283,10 @@ include third_party/stb/BUILD.mk # │ include third_party/mbedtls/BUILD.mk # │ include third_party/ncurses/BUILD.mk # │ include third_party/readline/BUILD.mk # │ +include third_party/libunwind/BUILD.mk # | +include third_party/libcxxabi/BUILD.mk # | include third_party/libcxx/BUILD.mk # │ +include third_party/double-conversion/BUILD.mk # │ include third_party/pcre/BUILD.mk # │ include third_party/less/BUILD.mk # │ include net/https/BUILD.mk # │ @@ -328,7 +330,6 @@ include third_party/python/BUILD.mk include tool/build/BUILD.mk include tool/curl/BUILD.mk include third_party/qemu/BUILD.mk -include third_party/libunwind/BUILD.mk include examples/BUILD.mk include examples/pyapp/BUILD.mk include examples/pylife/BUILD.mk @@ -441,6 +442,7 @@ COSMOPOLITAN_OBJECTS = \ LIBC_TIME \ THIRD_PARTY_MUSL \ THIRD_PARTY_ZLIB_GZ \ + THIRD_PARTY_LIBCXXABI \ THIRD_PARTY_LIBUNWIND \ LIBC_STDIO \ THIRD_PARTY_GDTOA \ diff --git a/libc/intrin/__cxa_pure_virtual.c b/libc/intrin/__cxa_pure_virtual.c deleted file mode 100644 index 5b79974fa..000000000 --- a/libc/intrin/__cxa_pure_virtual.c +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2023 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/kprintf.h" -#include "libc/runtime/runtime.h" - -void __cxa_pure_virtual(void) { -#ifndef NDEBUG - kprintf("__cxa_pure_virtual() called\n" - "Did you call a virtual method from a destructor?\n"); -#endif - abort(); -} diff --git a/libc/runtime/cxaguard.c b/libc/runtime/cxaguard.c deleted file mode 100644 index 44bf40349..000000000 --- a/libc/runtime/cxaguard.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "libc/atomic.h" -#include "libc/cxxabi.h" -#include "libc/intrin/atomic.h" -#include "libc/limits.h" -#include "third_party/nsync/futex.internal.h" - -// This file contains C++ ABI support functions for one time -// constructors as defined in the "Run-time ABI for the ARM Architecture" -// section 4.4.2 -// -// ARM C++ ABI and Itanium/x86 C++ ABI has different definition for -// one time construction: -// -// ARM C++ ABI defines the LSB of guard variable should be tested -// by compiler-generated code before calling __cxa_guard_acquire et al. -// -// The Itanium/x86 C++ ABI defines the low-order _byte_ should be -// tested instead. -// -// Meanwhile, guard variable are 32bit aligned for ARM, and 64bit -// aligned for x86. -// -// Reference documentation: -// -// section 3.2.3 of ARM IHI 0041C (for ARM) -// section 3.3.2 of the Itanium C++ ABI specification v1.83 (for x86). -// -// There is no C++ ABI available for other ARCH. But the gcc source -// shows all other ARCH follow the definition of Itanium/x86 C++ ABI. - -// The Itanium/x86 C++ ABI (used by all architectures that aren't ARM32) -// mandates guard variables are 64-bit aligned, 64-bit values. The least -// significant byte is tested by the compiler-generated code before it's -// calling __cxa_guard_acquire. -union CxaGuardValue { - atomic_int state; - int64_t aligner; -}; - -// Set construction state values according to reference documentation. 0 -// is the initialization value. Arm requires ((*gv & 1) == 1) after -// __cxa_guard_release, ((*gv & 3) == 0) after __cxa_guard_abort. X86 -// requires first byte not modified by __cxa_guard_acquire, first byte -// is non-zero after __cxa_guard_release. -#define CONSTRUCTION_NOT_YET_STARTED 0 -#define CONSTRUCTION_COMPLETE 1 -#define CONSTRUCTION_UNDERWAY_WITHOUT_WAITER 0x100 -#define CONSTRUCTION_UNDERWAY_WITH_WAITER 0x200 - -int __cxa_guard_acquire(union CxaGuardValue *gv) { - int old_value = atomic_load_explicit(&gv->state, memory_order_relaxed); - while (true) { - if (old_value == CONSTRUCTION_COMPLETE) { - // A load_acquire operation is needed before exiting with COMPLETE - // state, as we must ensure that all the stores performed by the - // construction function are observable on this CPU after we exit. - atomic_thread_fence(memory_order_acquire); - return 0; - } else if (old_value == CONSTRUCTION_NOT_YET_STARTED) { - if (!atomic_compare_exchange_weak_explicit( - &gv->state, &old_value, CONSTRUCTION_UNDERWAY_WITHOUT_WAITER, - memory_order_relaxed, memory_order_relaxed)) { - continue; - } - // The acquire fence may not be needed. But as described in section 3.3.2 - // of the Itanium C++ ABI specification, it probably has to behave like - // the acquisition of a mutex, which needs an acquire fence. - atomic_thread_fence(memory_order_acquire); - return 1; - } else if (old_value == CONSTRUCTION_UNDERWAY_WITHOUT_WAITER) { - if (!atomic_compare_exchange_weak_explicit( - &gv->state, &old_value, CONSTRUCTION_UNDERWAY_WITH_WAITER, - memory_order_relaxed, memory_order_relaxed)) { - continue; - } - } - nsync_futex_wait_(&gv->state, CONSTRUCTION_UNDERWAY_WITH_WAITER, 0, 0); - old_value = atomic_load_explicit(&gv->state, memory_order_relaxed); - } -} - -void __cxa_guard_release(union CxaGuardValue *gv) { - // Release fence is used to make all stores performed by the construction - // function visible in other threads. - int old_value = atomic_exchange_explicit(&gv->state, CONSTRUCTION_COMPLETE, - memory_order_release); - if (old_value == CONSTRUCTION_UNDERWAY_WITH_WAITER) { - nsync_futex_wake_(&gv->state, INT_MAX, 0); - } -} - -void __cxa_guard_abort(union CxaGuardValue *gv) { - // Release fence is used to make all stores performed by the construction - // function visible in other threads. - int old_value = atomic_exchange_explicit( - &gv->state, CONSTRUCTION_NOT_YET_STARTED, memory_order_release); - if (old_value == CONSTRUCTION_UNDERWAY_WITH_WAITER) { - nsync_futex_wake_(&gv->state, INT_MAX, 0); - } -} diff --git a/libc/thread/__cxa_thread_atexit.c b/libc/thread/__cxa_thread_atexit.c deleted file mode 100644 index 2cb38f71e..000000000 --- a/libc/thread/__cxa_thread_atexit.c +++ /dev/null @@ -1,23 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2023 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/cxaatexit.internal.h" - -int __cxa_thread_atexit(void *fun, void *arg, void *dso_symbol) { - return __cxa_thread_atexit_impl(fun, arg, dso_symbol); -} diff --git a/test/libc/thread/BUILD.mk b/test/libc/thread/BUILD.mk index d40095c18..5cadbff27 100644 --- a/test/libc/thread/BUILD.mk +++ b/test/libc/thread/BUILD.mk @@ -41,6 +41,7 @@ TEST_LIBC_THREAD_DIRECTDEPS = \ LIBC_THREAD \ LIBC_TIME \ LIBC_X \ + THIRD_PARTY_LIBCXXABI \ THIRD_PARTY_NSYNC \ THIRD_PARTY_NSYNC_MEM diff --git a/third_party/BUILD.mk b/third_party/BUILD.mk index 680e0d42b..be22c9d2a 100644 --- a/third_party/BUILD.mk +++ b/third_party/BUILD.mk @@ -17,6 +17,7 @@ o/$(MODE)/third_party: \ o/$(MODE)/third_party/hiredis \ o/$(MODE)/third_party/less \ o/$(MODE)/third_party/libcxx \ + o/$(MODE)/third_party/libcxxabi \ o/$(MODE)/third_party/libunwind \ o/$(MODE)/third_party/linenoise \ o/$(MODE)/third_party/lua \ diff --git a/third_party/double-conversion/BUILD.mk b/third_party/double-conversion/BUILD.mk index ee524beaf..0f80daa39 100644 --- a/third_party/double-conversion/BUILD.mk +++ b/third_party/double-conversion/BUILD.mk @@ -31,10 +31,9 @@ THIRD_PARTY_DOUBLECONVERSION_A_CHECKS = \ THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS = \ LIBC_INTRIN \ - LIBC_NEXGEN32E \ - LIBC_RUNTIME \ LIBC_STR \ - LIBC_TINYMATH + LIBC_TINYMATH \ + THIRD_PARTY_LIBCXXABI THIRD_PARTY_DOUBLECONVERSION_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/libcxx/BUILD.mk b/third_party/libcxx/BUILD.mk index d50c849dd..017183199 100644 --- a/third_party/libcxx/BUILD.mk +++ b/third_party/libcxx/BUILD.mk @@ -70,8 +70,8 @@ THIRD_PARTY_LIBCXX_A_HDRS = \ third_party/libcxx/deque \ third_party/libcxx/errno.h \ third_party/libcxx/exception \ - third_party/libcxx/exception_fallback.hh \ - third_party/libcxx/exception_pointer_unimplemented.hh \ + third_party/libcxx/exception_libcxxabi.hh \ + third_party/libcxx/exception_pointer_cxxabi.hh \ third_party/libcxx/execution \ third_party/libcxx/experimental/__config \ third_party/libcxx/filesystem \ @@ -99,7 +99,6 @@ THIRD_PARTY_LIBCXX_A_HDRS = \ third_party/libcxx/memory \ third_party/libcxx/mutex \ third_party/libcxx/new \ - third_party/libcxx/new_handler_fallback.hh \ third_party/libcxx/numeric \ third_party/libcxx/optional \ third_party/libcxx/ostream \ @@ -200,7 +199,9 @@ THIRD_PARTY_LIBCXX_A_DIRECTDEPS = \ LIBC_THREAD \ LIBC_TINYMATH \ THIRD_PARTY_COMPILER_RT \ - THIRD_PARTY_GDTOA + THIRD_PARTY_GDTOA \ + THIRD_PARTY_LIBCXXABI \ + THIRD_PARTY_LIBUNWIND THIRD_PARTY_LIBCXX_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_LIBCXX_A_DIRECTDEPS),$($(x)))) @@ -217,7 +218,10 @@ $(THIRD_PARTY_LIBCXX_A).pkg: \ $(THIRD_PARTY_LIBCXX_A_OBJS): private \ CXXFLAGS += \ -ffunction-sections \ - -fdata-sections + -fdata-sections \ + -fexceptions \ + -frtti \ + -DLIBCXX_BUILDING_LIBCXXABI THIRD_PARTY_LIBCXX_LIBS = $(foreach x,$(THIRD_PARTY_LIBCXX_ARTIFACTS),$($(x))) THIRD_PARTY_LIBCXX_SRCS = $(foreach x,$(THIRD_PARTY_LIBCXX_ARTIFACTS),$($(x)_SRCS)) diff --git a/third_party/libcxx/__config b/third_party/libcxx/__config index 7aa7c9a50..f6fa0de3e 100644 --- a/third_party/libcxx/__config +++ b/third_party/libcxx/__config @@ -12,7 +12,6 @@ #include "libc/isystem/features.h" #define _LIBCPP_ABI_UNSTABLE -#define _LIBCPP_NO_EXCEPTIONS #define _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER #define _LIBCPP_DISABLE_DEPRECATION_WARNINGS #define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION diff --git a/third_party/libcxx/exception.cc b/third_party/libcxx/exception.cc index 6bf1cf2f0..05987e9e7 100644 --- a/third_party/libcxx/exception.cc +++ b/third_party/libcxx/exception.cc @@ -11,6 +11,11 @@ #include "third_party/libcxx/new" #include "third_party/libcxx/typeinfo" -#include "third_party/libcxx/atomic_support.hh" -#include "third_party/libcxx/exception_fallback.hh" -#include "third_party/libcxx/exception_pointer_unimplemented.hh" +#if defined(LIBCXXRT) || defined(LIBCXX_BUILDING_LIBCXXABI) + #include "third_party/libcxxabi/include/cxxabi.h" + using namespace __cxxabiv1; + #define HAVE_DEPENDENT_EH_ABI 1 +#endif + +#include "third_party/libcxx/exception_libcxxabi.hh" +#include "third_party/libcxx/exception_pointer_cxxabi.hh" diff --git a/third_party/libcxx/exception_fallback.hh b/third_party/libcxx/exception_fallback.hh deleted file mode 100644 index 27afa53ee..000000000 --- a/third_party/libcxx/exception_fallback.hh +++ /dev/null @@ -1,111 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "third_party/libcxx/exception" -#include "third_party/libcxx/cstdio" -#include "third_party/libcxx/atomic_support.hh" - -namespace std { - -_LIBCPP_SAFE_STATIC static std::terminate_handler __terminate_handler; - -#if _LIBCPP_STD_VER <= 14 || \ - defined(_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS) || \ - defined(_LIBCPP_BUILDING_LIBRARY) -_LIBCPP_SAFE_STATIC static std::unexpected_handler __unexpected_handler; -// libcxxrt provides implementations of these functions itself. -unexpected_handler set_unexpected(unexpected_handler func) _NOEXCEPT { - return __libcpp_atomic_exchange(&__unexpected_handler, func); -} -unexpected_handler get_unexpected() _NOEXCEPT { - return __libcpp_atomic_load(&__unexpected_handler); -} -_LIBCPP_NORETURN -void unexpected() { - (*get_unexpected())(); - // unexpected handler should not return - terminate(); -} -#endif - -terminate_handler set_terminate(terminate_handler func) _NOEXCEPT { - return __libcpp_atomic_exchange(&__terminate_handler, func); -} - -terminate_handler get_terminate() _NOEXCEPT { - return __libcpp_atomic_load(&__terminate_handler); -} - -#ifndef __EMSCRIPTEN__ // We provide this in JS -_LIBCPP_NORETURN -void terminate() _NOEXCEPT { -#ifndef _LIBCPP_NO_EXCEPTIONS - try { -#endif // _LIBCPP_NO_EXCEPTIONS - (*get_terminate())(); - // handler should not return - fprintf(stderr, "terminate_handler unexpectedly returned\n"); - ::abort(); -#ifndef _LIBCPP_NO_EXCEPTIONS - } catch (...) { - // handler should not throw exception - fprintf(stderr, "terminate_handler unexpectedly threw an exception\n"); - ::abort(); - } -#endif // _LIBCPP_NO_EXCEPTIONS -} -#endif // !__EMSCRIPTEN__ - -#if !defined(__EMSCRIPTEN__) -bool uncaught_exception() _NOEXCEPT { return uncaught_exceptions() > 0; } - -int uncaught_exceptions() _NOEXCEPT { - // #warning uncaught_exception not yet implemented - fprintf(stderr, "uncaught_exceptions not yet implemented\n"); - ::abort(); -} -#endif // !__EMSCRIPTEN__ - -exception::~exception() _NOEXCEPT {} - -const char* exception::what() const _NOEXCEPT { return "std::exception"; } - -bad_exception::~bad_exception() _NOEXCEPT {} - -const char* bad_exception::what() const _NOEXCEPT { - return "std::bad_exception"; -} - -bad_alloc::bad_alloc() _NOEXCEPT {} - -bad_alloc::~bad_alloc() _NOEXCEPT {} - -const char* bad_alloc::what() const _NOEXCEPT { return "std::bad_alloc"; } - -bad_array_new_length::bad_array_new_length() _NOEXCEPT {} - -bad_array_new_length::~bad_array_new_length() _NOEXCEPT {} - -const char* bad_array_new_length::what() const _NOEXCEPT { - return "bad_array_new_length"; -} - -bad_cast::bad_cast() _NOEXCEPT {} - -bad_typeid::bad_typeid() _NOEXCEPT {} - -bad_cast::~bad_cast() _NOEXCEPT {} - -const char* bad_cast::what() const _NOEXCEPT { return "std::bad_cast"; } - -bad_typeid::~bad_typeid() _NOEXCEPT {} - -const char* bad_typeid::what() const _NOEXCEPT { return "std::bad_typeid"; } - -} // namespace std diff --git a/third_party/libcxx/new_handler_fallback.hh b/third_party/libcxx/exception_libcxxabi.hh similarity index 52% rename from third_party/libcxx/new_handler_fallback.hh rename to third_party/libcxx/exception_libcxxabi.hh index d98154357..9da417c44 100644 --- a/third_party/libcxx/new_handler_fallback.hh +++ b/third_party/libcxx/exception_libcxxabi.hh @@ -7,20 +7,21 @@ // //===----------------------------------------------------------------------===// -#include "third_party/libcxx/__config" -#include "third_party/libcxx/new" -#include "third_party/libcxx/atomic_support.hh" +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxx/exception" +using namespace __cxxabiv1; namespace std { -_LIBCPP_SAFE_STATIC static std::new_handler __new_handler; +bool uncaught_exception() _NOEXCEPT { return uncaught_exceptions() > 0; } -new_handler set_new_handler(new_handler handler) _NOEXCEPT { - return __libcpp_atomic_exchange(&__new_handler, handler); -} - -new_handler get_new_handler() _NOEXCEPT { - return __libcpp_atomic_load(&__new_handler); +int uncaught_exceptions() _NOEXCEPT +{ +# if _LIBCPPABI_VERSION > 1001 + return __cxa_uncaught_exceptions(); +# else + return __cxa_uncaught_exception() ? 1 : 0; +# endif } } // namespace std diff --git a/third_party/libcxx/exception_pointer_cxxabi.hh b/third_party/libcxx/exception_pointer_cxxabi.hh new file mode 100644 index 000000000..607bee012 --- /dev/null +++ b/third_party/libcxx/exception_pointer_cxxabi.hh @@ -0,0 +1,73 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxx/exception" +using namespace __cxxabiv1; + +namespace std { + +exception_ptr::~exception_ptr() _NOEXCEPT { + __cxa_decrement_exception_refcount(__ptr_); +} + +exception_ptr::exception_ptr(const exception_ptr& other) _NOEXCEPT + : __ptr_(other.__ptr_) +{ + __cxa_increment_exception_refcount(__ptr_); +} + +exception_ptr& exception_ptr::operator=(const exception_ptr& other) _NOEXCEPT +{ + if (__ptr_ != other.__ptr_) + { + __cxa_increment_exception_refcount(other.__ptr_); + __cxa_decrement_exception_refcount(__ptr_); + __ptr_ = other.__ptr_; + } + return *this; +} + +nested_exception::nested_exception() _NOEXCEPT + : __ptr_(current_exception()) +{ +} + +nested_exception::~nested_exception() _NOEXCEPT +{ +} + +_LIBCPP_NORETURN +void +nested_exception::rethrow_nested() const +{ + if (__ptr_ == nullptr) + terminate(); + rethrow_exception(__ptr_); +} + +exception_ptr current_exception() _NOEXCEPT +{ + // be nicer if there was a constructor that took a ptr, then + // this whole function would be just: + // return exception_ptr(__cxa_current_primary_exception()); + exception_ptr ptr; + ptr.__ptr_ = __cxa_current_primary_exception(); + return ptr; +} + +_LIBCPP_NORETURN +void rethrow_exception(exception_ptr p) +{ + __cxa_rethrow_primary_exception(p.__ptr_); + // if p.__ptr_ is NULL, above returns so we terminate + terminate(); +} + +} // namespace std diff --git a/third_party/libcxx/exception_pointer_unimplemented.hh b/third_party/libcxx/exception_pointer_unimplemented.hh deleted file mode 100644 index c54781d79..000000000 --- a/third_party/libcxx/exception_pointer_unimplemented.hh +++ /dev/null @@ -1,68 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "third_party/libcxx/stdio.h" -#include "third_party/libcxx/stdlib.h" -#include "third_party/libcxx/exception" - -namespace std { - -exception_ptr::~exception_ptr() _NOEXCEPT { - // #warning exception_ptr not yet implemented - fprintf(stderr, "exception_ptr not yet implemented\n"); - ::abort(); -} - -exception_ptr::exception_ptr(const exception_ptr& other) _NOEXCEPT - : __ptr_(other.__ptr_) { - // #warning exception_ptr not yet implemented - fprintf(stderr, "exception_ptr not yet implemented\n"); - ::abort(); -} - -exception_ptr& exception_ptr::operator=(const exception_ptr& other) _NOEXCEPT { - // #warning exception_ptr not yet implemented - fprintf(stderr, "exception_ptr not yet implemented\n"); - ::abort(); -} - -nested_exception::nested_exception() _NOEXCEPT : __ptr_(current_exception()) {} - -#if !defined(__GLIBCXX__) - -nested_exception::~nested_exception() _NOEXCEPT {} - -#endif - -_LIBCPP_NORETURN -void nested_exception::rethrow_nested() const { - // #warning exception_ptr not yet implemented - fprintf(stderr, "exception_ptr not yet implemented\n"); - ::abort(); -#if 0 - if (__ptr_ == nullptr) - terminate(); - rethrow_exception(__ptr_); -#endif // FIXME -} - -exception_ptr current_exception() _NOEXCEPT { - // #warning exception_ptr not yet implemented - fprintf(stderr, "exception_ptr not yet implemented\n"); - ::abort(); -} - -_LIBCPP_NORETURN -void rethrow_exception(exception_ptr p) { - // #warning exception_ptr not yet implemented - fprintf(stderr, "exception_ptr not yet implemented\n"); - ::abort(); -} - -} // namespace std diff --git a/third_party/libcxx/new.cc b/third_party/libcxx/new.cc index f52fb221c..44d08e0af 100644 --- a/third_party/libcxx/new.cc +++ b/third_party/libcxx/new.cc @@ -10,7 +10,7 @@ #include "third_party/libcxx/new" #include "third_party/libcxx/atomic_support.hh" -#include "third_party/libcxx/new_handler_fallback.hh" +#include "third_party/libcxxabi/include/cxxabi.h" namespace std { diff --git a/third_party/libcxxabi/BUILD.mk b/third_party/libcxxabi/BUILD.mk new file mode 100644 index 000000000..786691995 --- /dev/null +++ b/third_party/libcxxabi/BUILD.mk @@ -0,0 +1,112 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘ + +PKGS += THIRD_PARTY_LIBCXXABI + +THIRD_PARTY_LIBCXXABI_ARTIFACTS += THIRD_PARTY_LIBCXXABI_A +THIRD_PARTY_LIBCXXABI = $(THIRD_PARTY_LIBCXXABI_A_DEPS) $(THIRD_PARTY_LIBCXXABI_A) +THIRD_PARTY_LIBCXXABI_A = o/$(MODE)/third_party/libcxxabi/libcxxabi.a + +THIRD_PARTY_LIBCXXABI_A_INCS = \ + third_party/libcxxabi/demangle/ItaniumNodes.def \ + third_party/libcxxabi/aix_state_tab_eh.inc \ + third_party/libcxxabi/cxa_exception.h \ + third_party/libcxxabi/cxa_guard_impl.h \ + third_party/libcxxabi/cxa_handlers.h + +# TODO: Remove this aligned_alloc header when libcxx is updated +THIRD_PARTY_LIBCXXABI_A_HDRS = \ + third_party/libcxxabi/libcxx/include/__memory/aligned_alloc.h \ + third_party/libcxxabi/include/__cxxabi_config.h \ + third_party/libcxxabi/include/cxxabi.h \ + third_party/libcxxabi/demangle/DemangleConfig.h \ + third_party/libcxxabi/demangle/ItaniumDemangle.h \ + third_party/libcxxabi/demangle/StringViewExtras.h \ + third_party/libcxxabi/demangle/Utility.h \ + third_party/libcxxabi/abort_message.h \ + third_party/libcxxabi/fallback_malloc.h \ + third_party/libcxxabi/private_typeinfo.h + +THIRD_PARTY_LIBCXXABI_A_SRCS_CC = \ + third_party/libcxxabi/abort_message.cc \ + third_party/libcxxabi/cxa_aux_runtime.cc \ + third_party/libcxxabi/cxa_default_handlers.cc \ + third_party/libcxxabi/cxa_demangle.cc \ + third_party/libcxxabi/cxa_exception_storage.cc \ + third_party/libcxxabi/cxa_exception.cc \ + third_party/libcxxabi/cxa_guard.cc \ + third_party/libcxxabi/cxa_handlers.cc \ + third_party/libcxxabi/cxa_personality.cc \ + third_party/libcxxabi/cxa_thread_atexit.cc \ + third_party/libcxxabi/cxa_vector.cc \ + third_party/libcxxabi/cxa_virtual.cc \ + third_party/libcxxabi/fallback_malloc.cc \ + third_party/libcxxabi/private_typeinfo.cc \ + third_party/libcxxabi/stdlib_exception.cc \ + third_party/libcxxabi/stdlib_new_delete.cc \ + third_party/libcxxabi/stdlib_stdexcept.cc \ + third_party/libcxxabi/stdlib_typeinfo.cc + +THIRD_PARTY_LIBCXXABI_A_SRCS = \ + $(THIRD_PARTY_LIBCXXABI_A_SRCS_CC) + +THIRD_PARTY_LIBCXXABI_A_OBJS = \ + $(THIRD_PARTY_LIBCXXABI_A_SRCS_CC:%.cc=o/$(MODE)/%.o) + +THIRD_PARTY_LIBCXXABI_A_CHECKS = \ + $(THIRD_PARTY_LIBCXXABI_A).pkg \ + $(THIRD_PARTY_LIBCXXABI_A_HDRS:%=o/$(MODE)/%.okk) + +THIRD_PARTY_LIBCXXABI_A_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_THREAD \ + THIRD_PARTY_LIBUNWIND + +THIRD_PARTY_LIBCXXABI_A_DEPS := \ + $(call uniq,$(foreach x,$(THIRD_PARTY_LIBCXXABI_A_DIRECTDEPS),$($(x)))) + +$(THIRD_PARTY_LIBCXXABI_A): \ + third_party/libcxxabi/ \ + $(THIRD_PARTY_LIBCXXABI_A).pkg \ + $(THIRD_PARTY_LIBCXXABI_A_OBJS) + +$(THIRD_PARTY_LIBCXXABI_A).pkg: \ + $(THIRD_PARTY_LIBCXXABI_A_OBJS) \ + $(foreach x,$(THIRD_PARTY_LIBCXXABI_A_DIRECTDEPS),$($(x)_A).pkg) + +# TODO: Remove constinit hacks when we have C++20 +$(THIRD_PARTY_LIBCXXABI_A_OBJS): private \ + CXXFLAGS += \ + -ffunction-sections \ + -fdata-sections \ + -fexceptions \ + -frtti \ + -D_LIBCXXABI_BUILDING_LIBRARY \ + -D_LIBCPP_BUILDING_LIBRARY \ + -DHAVE___CXA_THREAD_ATEXIT_IMPL \ + -D_LIBCPP_CONSTINIT=__constinit \ + -Dconstinit=__constinit + +# ttypeIndex is initialized and used only when native_old_exception is true. +# GCC does not seem to allow this. +o/$(MODE)/third_party/libcxxabi/cxa_personality.o: private \ + CXXFLAGS += \ + -Wno-error=maybe-uninitialized + +THIRD_PARTY_LIBCXXABI_LIBS = $(foreach x,$(THIRD_PARTY_LIBCXXABI_ARTIFACTS),$($(x))) +THIRD_PARTY_LIBCXXABI_SRCS = $(foreach x,$(THIRD_PARTY_LIBCXXABI_ARTIFACTS),$($(x)_SRCS)) +THIRD_PARTY_LIBCXXABI_HDRS = $(foreach x,$(THIRD_PARTY_LIBCXXABI_ARTIFACTS),$($(x)_HDRS)) +THIRD_PARTY_LIBCXXABI_INCS = $(foreach x,$(THIRD_PARTY_LIBCXXABI_ARTIFACTS),$($(x)_INCS)) +THIRD_PARTY_LIBCXXABI_CHECKS = $(foreach x,$(THIRD_PARTY_LIBCXXABI_ARTIFACTS),$($(x)_CHECKS)) +THIRD_PARTY_LIBCXXABI_OBJS = $(foreach x,$(THIRD_PARTY_LIBCXXABI_ARTIFACTS),$($(x)_OBJS)) + +.PHONY: o/$(MODE)/third_party/libcxxabi +o/$(MODE)/third_party/libcxxabi: \ + $(THIRD_PARTY_LIBCXXABI_CHECKS) \ + $(THIRD_PARTY_LIBCXXABI_A) diff --git a/third_party/libcxxabi/CREDITS.TXT b/third_party/libcxxabi/CREDITS.TXT new file mode 100644 index 000000000..fbb6cbf38 --- /dev/null +++ b/third_party/libcxxabi/CREDITS.TXT @@ -0,0 +1,71 @@ +This file is a partial list of people who have contributed to the LLVM/libc++abi +project. If you have contributed a patch or made some other contribution to +LLVM/libc++abi, please submit a patch to this file to add yourself, and it will be +done! + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address +(S). + +N: Aaron Ballman +E: aaron@aaronballman.com +D: Minor patches + +N: Logan Chien +E: logan.chien@mediatek.com +D: ARM EHABI Unwind & Exception Handling + +N: Marshall Clow +E: mclow.lists@gmail.com +E: marshall@idio.com +D: Architect and primary coauthor of libc++abi + +N: Matthew Dempsky +E: matthew@dempsky.org +D: Minor patches and bug fixes. + +N: Nowar Gu +E: wenhan.gu@gmail.com +D: Minor patches and fixes + +N: Howard Hinnant +E: hhinnant@apple.com +D: Architect and primary coauthor of libc++abi + +N: Dana Jansens +E: danakj@chromium.org +D: ARM EHABI Unwind & Exception Handling + +N: Nick Kledzik +E: kledzik@apple.com + +N: Antoine Labour +E: piman@chromium.org +D: ARM EHABI Unwind & Exception Handling + +N: Bruce Mitchener, Jr. +E: bruce.mitchener@gmail.com +D: Minor typo fixes + +N: Andrew Morrow +E: andrew.c.morrow@gmail.com +D: Minor patches and fixes + +N: Erik Olofsson +E: erik.olofsson@hansoft.se +E: erik@olofsson.info +D: Minor patches and fixes + +N: Jon Roelofs +E: jroelofs@jroelofs.com +D: ARM EHABI Unwind & Exception Handling, Bare-metal + +N: Nico Weber +E: thakis@chromium.org +D: ARM EHABI Unwind & Exception Handling + +N: Albert J. Wong +E: ajwong@google.com +D: ARM EHABI Unwind & Exception Handling + diff --git a/third_party/libcxxabi/LICENSE.TXT b/third_party/libcxxabi/LICENSE.TXT new file mode 100644 index 000000000..b75c0441b --- /dev/null +++ b/third_party/libcxxabi/LICENSE.TXT @@ -0,0 +1,311 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== + +The libc++abi library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/libcxxabi/README.cosmo b/third_party/libcxxabi/README.cosmo new file mode 100644 index 000000000..41e62149b --- /dev/null +++ b/third_party/libcxxabi/README.cosmo @@ -0,0 +1,18 @@ +DESCRIPTION + + libcxxabi - C++ Standard Library Support + +ORIGIN + + Obtained from the LLVM monorepo, release 17.0.6. + + https://github.com/llvm/llvm-project/tree/llvmorg-17.0.6/libunwind + commit 6009708b4367171ccdbf4b5905cb6a803753fe18 + Author: Tobias Hieta + Date: Tue, 28 Nov 2023 09:52:28 +0100 + +LOCAL CHANGES + + - Add <__memory/aligned_alloc.h> (contains a few inline functions) from + upstream libcxx. + - Enable __cxa_thread_atexit for Cosmopolitan. diff --git a/third_party/libcxxabi/abort_message.cc b/third_party/libcxxabi/abort_message.cc new file mode 100644 index 000000000..b313ab610 --- /dev/null +++ b/third_party/libcxxabi/abort_message.cc @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "libc/isystem/stdlib.h" +#include "libc/stdio/stdio.h" +#include "libc/isystem/stdarg.h" +#include "third_party/libcxxabi/abort_message.h" + +#ifdef __BIONIC__ +# include +# if __ANDROID_API__ >= 21 +# include + extern "C" void android_set_abort_message(const char* msg); +# else +# include +# endif // __ANDROID_API__ >= 21 +#endif // __BIONIC__ + +#if defined(__APPLE__) && __has_include() +# include +# define _LIBCXXABI_USE_CRASHREPORTER_CLIENT +#endif + +void abort_message(const char* format, ...) +{ + // Write message to stderr. We do this before formatting into a + // variable-size buffer so that we still get some information if + // formatting into the variable-sized buffer fails. +#if !defined(NDEBUG) || !defined(LIBCXXABI_BAREMETAL) + { + fprintf(stderr, "libc++abi: "); + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); + } +#endif + + // Format the arguments into an allocated buffer. We leak the buffer on + // purpose, since we're about to abort() anyway. +#if defined(_LIBCXXABI_USE_CRASHREPORTER_CLIENT) + char* buffer; + va_list list; + va_start(list, format); + vasprintf(&buffer, format, list); + va_end(list); + + CRSetCrashLogMessage(buffer); +#elif defined(__BIONIC__) + char* buffer; + va_list list; + va_start(list, format); + vasprintf(&buffer, format, list); + va_end(list); + +# if __ANDROID_API__ >= 21 + // Show error in tombstone. + android_set_abort_message(buffer); + + // Show error in logcat. + openlog("libc++abi", 0, 0); + syslog(LOG_CRIT, "%s", buffer); + closelog(); +# else + // The good error reporting wasn't available in Android until L. Since we're + // about to abort anyway, just call __assert2, which will log _somewhere_ + // (tombstone and/or logcat) in older releases. + __assert2(__FILE__, __LINE__, __func__, buffer); +# endif // __ANDROID_API__ >= 21 +#endif // __BIONIC__ + + abort(); +} diff --git a/third_party/libcxxabi/abort_message.h b/third_party/libcxxabi/abort_message.h new file mode 100644 index 000000000..790a445fa --- /dev/null +++ b/third_party/libcxxabi/abort_message.h @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __ABORT_MESSAGE_H_ +#define __ABORT_MESSAGE_H_ + +#include "third_party/libcxxabi/include/cxxabi.h" + +extern "C" _LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void +abort_message(const char *format, ...) __attribute__((format(printf, 1, 2))); + +#endif diff --git a/third_party/libcxxabi/aix_state_tab_eh.inc b/third_party/libcxxabi/aix_state_tab_eh.inc new file mode 100644 index 000000000..3bb09ed27 --- /dev/null +++ b/third_party/libcxxabi/aix_state_tab_eh.inc @@ -0,0 +1,745 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the personality and helper functions for the state +// table based EH used by IBM legacy compilers xlC and xlclang++ on AIX. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +/* + The legacy IBM xlC and xlclang++ compilers use the state table for EH + instead of the range table. Destructors, or addresses of the possible catch + sites or cleanup code are specified in the state table which is a finite + state machine (FSM). Each function that has a state table also has an + autolocal state variable. The state variable represents the current state + of the function for EH and is found through the traceback table of the + function during unwinding, which is located at the end of each function. + The FSM is an array of state entries. Each state entry has the following + fields: + + * offset/address/pointer - the offset used to locate the object, or the + address of a global object, or the address of the next state if it is an + old conditional state change entry; + * dtor/landing pad - address of the destructor function to invoke, + or address of the catch block or cleanup code in the user code to branch to; + * element count/action flag - the number of elements or the flag for actions; + * element size - if the object is an array this is the size of one element + of the array; + * flags - flags used to control how fields in the entry are interpreted; + * next state - the state to execute next after the action for this state is + performed. The value of zero indicates the end of the state for this + function. + + The following is the description of 'element count/action flag' field. ++-----------------------------------------------------------------------------+ +| value | description | action | ++-------+------------------------+--------------------------------------------+ +| > 1 | object is an array | calls __cxa_vec_cleanup to run dtor for | +| | | each member of the array | ++-------+------------------------+--------------------------------------------+ +| 1, 0 | object is a scalar | calls dtor for the object | ++-------+------------------------+--------------------------------------------+ +| -1 | begin catch | branches to the handler which performes | +| | | catch-match. If there is no catch that | +| | | matches the exception it will be rethrown | ++-------+------------------------+--------------------------------------------+ +| -2 | end catch | ends current catch block and continues | +| | | attempting to catch the exception | ++-------+------------------------+--------------------------------------------+ +| -3 | delete the object | calls the delete function of the object | ++-------+------------------------+--------------------------------------------+ +| -4 | cleanup label | branches to the user code for cleaning up | ++-------+------------------------+--------------------------------------------+ +*/ + +namespace __cxxabiv1 { + +extern "C" { + +// Macros for debugging the state table parsing. +#ifdef NDEBUG +# define _LIBCXXABI_TRACE_STATETAB(msg, ...) +# define _LIBCXXABI_TRACE_STATETAB0(msg) +# define _LIBCXXABI_TRACE_STATETAB1(msg) +# define _LIBCXXABI_TRACING_STATETAB 0 +#else +static bool state_tab_dbg() { + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBCXXABI_PRINT_STATTAB") != NULL); + checked = true; + } + return log; +} + +# define _LIBCXXABI_TRACE_STATETAB(msg, ...) \ + do { \ + if (state_tab_dbg()) \ + fprintf(stderr, "libcxxabi: " msg, __VA_ARGS__); \ + } while (0) +# define _LIBCXXABI_TRACE_STATETAB0(msg) \ + do { \ + if (state_tab_dbg()) \ + fprintf(stderr, "libcxxabi: " msg); \ + } while (0) +# define _LIBCXXABI_TRACE_STATETAB1(msg) \ + do { \ + if (state_tab_dbg()) \ + fprintf(stderr, msg); \ + } while (0) + +# define _LIBCXXABI_TRACING_STATETAB state_tab_dbg() +#endif // NDEBUG + +namespace __state_table_eh { + +using destruct_f = void (*)(void*); + +// Definition of flags for the state table entry field 'action flag'. +enum FSMEntryCount : intptr_t { beginCatch = -1, endCatch = -2, deleteObject = -3, cleanupLabel = -4, terminate = -5 }; + +// Definition of flags for the state table entry field 'flags'. +enum FSMEntryFlag : int16_t { + indirect = 0x100, // Object was thrown from a function where + // the return value optimization was used. + oldConditionalStateChange = 0x400, // State table entry is an indirect state + // change, dereference the address in + // offset as int for the target state. + // This is deprecated. This indicates + // the address is direct. (static local). + conditionalStateChange = 0x800, // State table entry is an indirect state + // change, dereference the address in + // offset as int for the target state. + // The temporary is an automatic. State + // change is used in cases such as + // (b?(T1(),foo()):(T2(),foo())),throw 42; + // which causes a conditional state change + // so that we know if T1 or T2 need to be + // destroyed. + thisFlag = 0x01, // The address of the object for the + // cleanup action is based on the + // StateVariable::thisValue. + vBaseFlag = 0x02, // The object is of a virtual base class. + globalObj = 0x04 // FSMEntry::address is the address of + // a global object. +}; + +namespace { +// The finite state machine to be walked. +struct FSMEntry { + union { + // Offset of the object within its stack frame or containing object. + intptr_t offset; + // Address of a global object. + intptr_t address; + // Address of the next state if it is an old conditional state change entry. + intptr_t nextStatePtr; + }; + union { + // Address of the destructor function. + void (*destructor)(void*, size_t); + // The address of the catch block or cleanup code. + void* landingPad; + }; + union { + // The flag for actions (when the value is negative). + FSMEntryCount actionFlag; + // The element count (when the value is positive or zero). + size_t elementCount; + }; + size_t elemSize; + FSMEntryFlag flags; + uint16_t nextState; +}; + +struct FSM { + uint32_t magic; // Magic number of the state table. + int32_t numberOfStates; + FSMEntry table[1]; // Actually table[numberOfStates]. +}; + +// The state variable on the stack. +struct StateVariable { + int32_t state; + struct FSM* table; + intptr_t thisValue; + int32_t ignoreVBasePtrs; +}; +} // namespace + +// State table magic number +enum FSMMagic : uint32_t { + number = 0xbeefdead, // State table generated by xlC compiler. + number2 = 0xbeeedead, // State table generated by early version xlC compiler. + number3 = 0x1cedbeef // State table generated by xlclang++ compiler. +}; + +constexpr size_t dtorArgument = 0x02; // Flag to destructor indicating to free + // virtual bases, don't delete object. + +static void invoke_destructor(FSMEntry* fsmEntry, void* addr) { + _LIBCXXABI_TRACE_STATETAB("Destruct object=%p, fsmEntry=%p\n", addr, reinterpret_cast(fsmEntry)); + try { + if (fsmEntry->elementCount == 1) { + _LIBCXXABI_TRACE_STATETAB0("calling scalar destructor\n"); + (*fsmEntry->destructor)(addr, dtorArgument); + _LIBCXXABI_TRACE_STATETAB0("returned from scalar destructor\n"); + } else { + _LIBCXXABI_TRACE_STATETAB0("calling vector destructor\n"); + __cxa_vec_cleanup(addr, reinterpret_cast(fsmEntry->elementCount), fsmEntry->elemSize, + reinterpret_cast(fsmEntry->destructor)); + _LIBCXXABI_TRACE_STATETAB0("returned from vector destructor\n"); + } + } catch (...) { + _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in destructor, terminating\n"); + std::terminate(); + } +} + +static void invoke_delete(FSMEntry* fsmEntry, void* addr) { + char* objectAddress = *reinterpret_cast(addr); + + _LIBCXXABI_TRACE_STATETAB("Delete object=%p, fsmEntry=%p\n", reinterpret_cast(objectAddress), + reinterpret_cast(fsmEntry)); + try { + _LIBCXXABI_TRACE_STATETAB0("..calling delete()\n"); + // 'destructor' holds a function pointer to delete(). + (*fsmEntry->destructor)(objectAddress, fsmEntry->elemSize); + _LIBCXXABI_TRACE_STATETAB0("..returned from delete()\n"); + } catch (...) { + _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in delete(), terminating\n"); + std::terminate(); + } +} + +// Get the frame address of the current function from its traceback table +// which is at the end of each function. +static uintptr_t get_frame_addr(_Unwind_Context* context) { + int framePointerReg = 1; // default frame pointer == SP. + uint32_t* p = reinterpret_cast(_Unwind_GetIP(context)); + + // Keep looking forward until a word of 0 is found. The traceback + // table starts at the following word. + while (*p) + ++p; + tbtable* TBTable = reinterpret_cast(p + 1); + + p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if it exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + ++p; + + // Skip field tb_offset if it exists. + if (TBTable->tb.has_tboff) + ++p; + + // Skip field hand_mask if it exists. + if (TBTable->tb.int_hndl) + ++p; + + // Skip fields ctl_info and ctl_info_disp if they exist. + if (TBTable->tb.has_ctl) + p += 1 + *p; + + // Skip fields name_len and name if exist. + if (TBTable->tb.name_present) { + const uint16_t name_len = *reinterpret_cast(p); + p = reinterpret_cast(reinterpret_cast(p) + name_len + sizeof(uint16_t)); + } + + if (TBTable->tb.uses_alloca) + framePointerReg = *reinterpret_cast(p); + + return _Unwind_GetGR(context, framePointerReg); +} + +// Calculate the object address from the FSM entry. +static void* compute_addr_from_table(FSMEntry* fsmEntry, StateVariable* const state, _Unwind_Context* context) { + void* addr; + if (fsmEntry->flags & FSMEntryFlag::globalObj) { + addr = reinterpret_cast(fsmEntry->address); + _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) addr=fsmEntry->address=%p\n", addr); + } else if (fsmEntry->flags & FSMEntryFlag::thisFlag) { + addr = reinterpret_cast(state->thisValue + fsmEntry->offset); + _LIBCXXABI_TRACE_STATETAB("Address calculation (this obj) fsmEntry->offset=%ld : " + "state->thisValue=%ld addr=(fsmEntry->offset+state->thisValue)=%p\n", + fsmEntry->offset, state->thisValue, addr); + } else if (fsmEntry->flags & FSMEntryFlag::indirect) { + addr = reinterpret_cast( + *reinterpret_cast(get_frame_addr(context) + static_cast(fsmEntry->offset))); + _LIBCXXABI_TRACE_STATETAB("Address calculation (indirect obj) addr=%p, fsmEntry->offset=%ld \n", + addr, fsmEntry->offset); + } else { + addr = reinterpret_cast(get_frame_addr(context) + static_cast(fsmEntry->offset)); + _LIBCXXABI_TRACE_STATETAB("Address calculation. (local obj) addr=fsmEntry->offset=%p\n", + addr); + } + return addr; +} + +static void scan_state_tab(scan_results& results, _Unwind_Action actions, bool native_exception, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) { + // Initialize results to found nothing but an error. + results.ttypeIndex = 0; + results.actionRecord = 0; + results.languageSpecificData = 0; + results.landingPad = 0; + results.adjustedPtr = 0; + results.reason = _URC_FATAL_PHASE1_ERROR; + + // Check for consistent actions. + if (actions & _UA_SEARCH_PHASE) { + // Do Phase 1 + if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) { + // None of these flags should be set during Phase 1. + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + } else if (actions & _UA_CLEANUP_PHASE) { + if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) { + // _UA_HANDLER_FRAME should only be set if phase 1 found a handler. + // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened. + // Client error + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } + } else { + // Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set. + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + + if (_LIBCXXABI_TRACING_STATETAB) { + _LIBCXXABI_TRACE_STATETAB1("\n"); + _LIBCXXABI_TRACE_STATETAB("%s: actions=%d (", __func__, actions); + + if (_UA_SEARCH_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_SEARCH_PHASE "); + if (_UA_CLEANUP_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_CLEANUP_PHASE "); + if (_UA_HANDLER_FRAME & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_HANDLER_FRAME "); + if (_UA_FORCE_UNWIND & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_FORCE_UNWIND "); + _LIBCXXABI_TRACE_STATETAB1(")\n"); + _LIBCXXABI_TRACE_STATETAB(" unwind_exception=%p context=%p\n", reinterpret_cast(unwind_exception), + reinterpret_cast(context)); + } + + // Start scan by getting state table address. + StateVariable* const state = reinterpret_cast(_Unwind_GetLanguageSpecificData(context)); + if (state->state <= 0) { + // The state is not correct - give up on this routine. + _LIBCXXABI_TRACE_STATETAB("state=%d and is <= 0), continue unwinding\n", state->state); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + // Parse the state table. + FSM* const fsm = state->table; + FSMEntry* currFSMEntry; + + if (fsm->magic != FSMMagic::number && fsm->magic != FSMMagic::number2 && fsm->magic != FSMMagic::number3) { + // Something is wrong with the state table we found. + if (_UA_SEARCH_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE1_ERROR\n"); + results.reason = _URC_FATAL_PHASE1_ERROR; + } else if (_UA_CLEANUP_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + } else { + // We should never get here. + _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table + RT Internal error, return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + } + return; + } + + if (_LIBCXXABI_TRACING_STATETAB) { + // Print the state table for debugging purposes. + _LIBCXXABI_TRACE_STATETAB("state->state=%d, state->ignoreVBasePtrs=%d\n", state->state, state->ignoreVBasePtrs); + _LIBCXXABI_TRACE_STATETAB("fsm->magic=%#x, fsm->numberOfStates=%d\n", fsm->magic, fsm->numberOfStates); + // Print out the FSM table. + _LIBCXXABI_TRACE_STATETAB0("FSM table:\n"); + _LIBCXXABI_TRACE_STATETAB("%12s %10s %8s %10s %7s %7s %7s %7s\n", "Entry Addr", "state", "Offset", "DTR/lpad", + "count", "el_size", "flags", "next"); + for (int i = 0; i < fsm->numberOfStates; i++) { + currFSMEntry = &fsm->table[i]; + _LIBCXXABI_TRACE_STATETAB("%12p (%8d) %8ld %10p %7ld " + "%7ld %#7x %7d\n", + reinterpret_cast(&currFSMEntry), i + 1, currFSMEntry->offset, + reinterpret_cast(currFSMEntry->destructor), + currFSMEntry->elementCount, currFSMEntry->elemSize, currFSMEntry->flags, + currFSMEntry->nextState); + } + } + + if (_UA_SEARCH_PHASE & actions) { + // Start walking the state table. Use a local copy of state->state so when + // we return from search phase we don't change the state number. + int currState = state->state; + + while (currState > 0) { + currFSMEntry = &fsm->table[currState - 1]; + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", currState, currFSMEntry->flags); + + if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch) { + // Found a catch handler. + if (fsm->magic == FSMMagic::number) { + _LIBCXXABI_TRACE_STATETAB0("Found a xlC catch handler, return _URC_FATAL_PHASE1_ERROR\n"); + // xlC catch handlers cannot be entered because they use a + // proprietary EH runtime that is not interoperable. + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + // xlclang++ compiled frames use CXA-abi EH calls and any catch + // block will include a catch(...) block so it is safe to assume that + // the handler is found without checking the catch match. The + // catch(...) block will rethrow the exception if there isn't a + // match. + _LIBCXXABI_TRACE_STATETAB0("Found a catch handler, return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } + if (currFSMEntry->actionFlag == FSMEntryCount::terminate) { + _LIBCXXABI_TRACE_STATETAB0("Found the terminate state, return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } + if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) { + // Deprecated conditional expression. + currState = *reinterpret_cast(currFSMEntry->nextStatePtr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference " + "currFSMEntry->nextStatePtr(%ld), set state=%d\n", + currFSMEntry->nextStatePtr, currState); + continue; // We are done this iteration of the loop, since + // we changed a state. + } + if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) { + void* addr = compute_addr_from_table(currFSMEntry, state, context); + currState = *reinterpret_cast(addr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, dereference " + "addr(%p), set state=%d\n", addr, currState); + continue; // We are done this iteration of the loop, since we + // changed the state. + } + // Go to the next state. + currState = currFSMEntry->nextState; + } + _LIBCXXABI_TRACE_STATETAB0("No catch handler found, return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + if (_UA_CLEANUP_PHASE & actions) { + // Start walking the state table. + while (state->state > 0) { + currFSMEntry = &fsm->table[state->state - 1]; + + if (currFSMEntry->actionFlag == FSMEntryCount::terminate) { + _LIBCXXABI_TRACE_STATETAB0("Reached terminate state. Call terminate.\n"); + std::terminate(); + } + // Perform action according to the currFSMEntry->actionFlag, + // except when flag is FSMEntryFlag::conditionalStateChange or + // FSMEntryFlag::oldConditionalStateChange. + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", state->state, currFSMEntry->flags); + if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) { + state->state = *reinterpret_cast(currFSMEntry->nextStatePtr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference " + "currFSMEntry->nextStatePtr(%ld), set state=%d\n", + currFSMEntry->nextStatePtr, state->state); + continue; // We are done with this iteration of the loop, since we changed a state. + } + if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) { + // A conditional state table entry holds the address of a local + // that holds the next state. + void* addr = compute_addr_from_table(currFSMEntry, state, context); + state->state = *reinterpret_cast(addr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, dereference " + "addr(%p), set state=%d\n", addr, state->state); + continue; // We are done with this iteration of the loop, since we changed a state. + } + if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch || currFSMEntry->actionFlag == FSMEntryCount::endCatch || + currFSMEntry->actionFlag == FSMEntryCount::cleanupLabel) { + + _LIBCXXABI_TRACE_STATETAB( + "FSMEntryCount::%s: handler %p/%p, return _URC_HANDLER_FOUND\n", + (currFSMEntry->actionFlag == FSMEntryCount::beginCatch + ? "beginCatch" + : (currFSMEntry->actionFlag == FSMEntryCount::endCatch ? "endCatch" : "cleanupLabel")), + currFSMEntry->landingPad, *reinterpret_cast(currFSMEntry->landingPad)); + + state->state = currFSMEntry->nextState; + results.landingPad = reinterpret_cast(*reinterpret_cast(currFSMEntry->landingPad)); + results.reason = _URC_HANDLER_FOUND; + return; + } + if (currFSMEntry->elementCount > 0) { + if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag && state->ignoreVBasePtrs) { + _LIBCXXABI_TRACE_STATETAB0("Ignoring virtual base dtor.\n"); + } else { + // We need to invoke the virtual base destructor. This must be + // a frame from the legacy xlC compiler as the xlclang++ compiler + // generates inline cleanup code rather than specifying + // the destructor via the state table. + void* addr = compute_addr_from_table(currFSMEntry, state, context); + + // An extra indirect to get to the object according to the object + // model used by the xlC compiler. + addr = reinterpret_cast(*reinterpret_cast(addr)); + _LIBCXXABI_TRACE_STATETAB("Invoke dtor for object=%p\n", addr); + invoke_destructor(currFSMEntry, addr); + } + } else if (currFSMEntry->actionFlag == FSMEntryCount::deleteObject) { + void* addr = compute_addr_from_table(currFSMEntry, state, context); + if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag) { + // We need to invoke the virtual base delete function. This must be + // a frame from the legacy xlC compiler as the xlclang++ compiler + // generates inline cleanup code rather than specifying + // the delete function via the state table. + + // An extra indirect to get to the object according to the object + // model used by the xlC compiler. + addr = reinterpret_cast(*reinterpret_cast(addr)); + } + _LIBCXXABI_TRACE_STATETAB("Delete object at %p\n", addr); + invoke_delete(currFSMEntry, addr); + } else { + _LIBCXXABI_TRACE_STATETAB("Unknown entry in FSM (count=%ld), ignored\n", + currFSMEntry->elementCount); + } // End of action switching. + + // Go to next state. + state->state = currFSMEntry->nextState; + } + _LIBCXXABI_TRACE_STATETAB0("No catch handler, return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + _LIBCXXABI_TRACE_STATETAB0("No state table entry for this exception, call_terminate()\n"); + // It is possible that no state table entry specify how to handle + // this exception. By spec, terminate it immediately. + call_terminate(native_exception, unwind_exception); +} + +// Personality routine for EH using the state table. +_LIBCXXABI_FUNC_VIS _Unwind_Reason_Code +__xlcxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) { + if (version != 1 || unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + + bool native_exception = (exceptionClass & get_vendor_and_language) == (kOurExceptionClass & get_vendor_and_language); + scan_results results; + scan_state_tab(results, actions, native_exception, unwind_exception, context); + if (actions & _UA_SEARCH_PHASE) { + // Phase 1 search: All we're looking for in phase 1 is a handler that + // halts unwinding + return results.reason; + } + if (actions & _UA_CLEANUP_PHASE) { + // Phase 2 cleanup: + if (results.reason == _URC_HANDLER_FOUND) { + // Store the address of unwind_exception in the stack field + // reserved for compilers (SP + 3 * sizeof(uintptr_t)) in the stack of + // the caller of the function containing the landing pad (within the link + // area for the call to the latter) for __xlc_exception_handle() + // to retrieve when it is called by the landing pad. + uintptr_t *currentSP = reinterpret_cast(_Unwind_GetGR(context, 1)); + uintptr_t *callersSP = reinterpret_cast(currentSP[0]); + callersSP[3] = reinterpret_cast(unwind_exception); + _LIBCXXABI_TRACE_STATETAB("Handshake: save unwind_exception=%p in stack=%p\n", + reinterpret_cast(unwind_exception), reinterpret_cast(callersSP)); + // Jump to the handler. + _Unwind_SetIP(context, results.landingPad); + return _URC_INSTALL_CONTEXT; + } + // Did not find a handler. Return the results of the scan. Normally + // _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE2_ERROR. + return results.reason; + } + // We were called improperly: neither a phase 1 or phase 2 search. + return _URC_FATAL_PHASE1_ERROR; +} +} // namespace __state_table_eh + +// The following are EH helper functions for xlclang++ compiled code. + +// __xlc_catch_matchv2 +// Check whether the thrown object matches the catch handler's exception +// declaration. If there is a match, the function returns true with adjusted +// address of the thrown object. Otherwise, returns false. +_LIBCXXABI_FUNC_VIS bool +__xlc_catch_matchv2(_Unwind_Exception* exceptionObject, std::type_info* catchTypeInfo, void*& obj) { + _LIBCXXABI_TRACE_STATETAB("Entering %s, exceptionObject=%p\n", __func__, reinterpret_cast(exceptionObject)); + + if (!__isOurExceptionClass(exceptionObject)) { + _LIBCXXABI_TRACE_STATETAB0("No match, not a C++ exception\n"); + return false; + } + + __cxa_exception* exceptionHeader = 0; + + if (__getExceptionClass(exceptionObject) == kOurDependentExceptionClass) { + // Walk to the __cxa_dependent_exception primary exception for the + // exception object and its type_info. + __cxa_dependent_exception* dependentExceptionHeader = + reinterpret_cast<__cxa_dependent_exception*>(exceptionObject + 1) - 1; + exceptionHeader = reinterpret_cast<__cxa_exception*>(dependentExceptionHeader->primaryException) - 1; + _LIBCXXABI_TRACE_STATETAB("exceptionObject 0x%p is a dependent, primary 0x%p\n", + reinterpret_cast(exceptionObject), + reinterpret_cast(&exceptionHeader->unwindHeader)); + exceptionObject = &exceptionHeader->unwindHeader; + } else { + _LIBCXXABI_TRACE_STATETAB("exceptionObject %p is NOT a dependent\n", reinterpret_cast(exceptionObject)); + exceptionHeader = reinterpret_cast<__cxa_exception*>(exceptionObject + 1) - 1; + } + + void* thrownObject = reinterpret_cast(exceptionObject + 1); + std::type_info* throwTypeInfo = exceptionHeader->exceptionType; + + // Get the type info for the thrown type and this catch clause and + // see if the catch caluse can catch that type. + + __cxxabiv1::__shim_type_info* catchType = reinterpret_cast<__cxxabiv1::__shim_type_info*>(catchTypeInfo); + __cxxabiv1::__shim_type_info* throwType = reinterpret_cast<__cxxabiv1::__shim_type_info*>(throwTypeInfo); + _LIBCXXABI_TRACE_STATETAB("UnwindException=%p, thrownObject=%p, throwTypeInfo=%p(%s), catchTypeInfo=%p(%s)\n", + reinterpret_cast(exceptionObject), thrownObject, reinterpret_cast(throwType), + throwType->name(), reinterpret_cast(catchType), catchType->name()); + if (catchType->can_catch(throwType, thrownObject)) { + exceptionHeader->adjustedPtr = thrownObject; + obj = thrownObject; + _LIBCXXABI_TRACE_STATETAB("Match found for thrownObject=%p\n", thrownObject); + return true; + } + _LIBCXXABI_TRACE_STATETAB0("No match\n"); + return false; +} + +// __xlc_throw_badexception +// This function is for xlclang++. It allocates and throws a bad_exception. +// During unwinding for this bad_exception, the previous exception which is +// not matching the throw spec will be cleaned up. Thus having the same +// effect as replace the top most exception (which is bad) with a bad_exception. +_LIBCXXABI_FUNC_VIS void __xlc_throw_badexception() { + _LIBCXXABI_TRACE_STATETAB("Entering function: %s\n\n", __func__); + void* newexception = new (__cxa_allocate_exception(sizeof(std::bad_exception))) std::bad_exception; + __cxa_throw(newexception, const_cast(&typeid(std::bad_exception)), 0); +} + +// skip_non_cxx_eh_aware_frames +// This function skips non-C++ EH aware stack frames by unwinding from the +// stack frame pointed by 'Sp' and returns the first C++ EH aware stack frame +// found. 'Pc' is an instruction address inside the function that owns the +// stack frame pointed to by 'Sp'. +static uintptr_t* skip_non_cxx_eh_aware_frames(uint32_t* Pc, uintptr_t* Sp) { + uint32_t* currentPc = Pc; + uintptr_t* currentStack = Sp; + + // Loop until a C++ EH aware frame is found or the return address is 0, + // which is the return address of the startup function '__start'. + while (currentPc != 0) { + uint32_t* p = currentPc; + + // Keep looking forward until a word of 0 is found. The traceback + // table starts at the following word. + while (*p) + ++p; + tbtable* TBTable = reinterpret_cast(p + 1); + + // A stack frame with a C++ state table is C++ EH aware. + if (TBTable->tb.lang == TB_CPLUSPLUS && TBTable->tb.has_ctl) + return currentStack; + + // Move up one stack frame. + currentStack = reinterpret_cast(currentStack[0]); + // Get the value of the LR (saved, prior to incrementing the SP, by the + // prolog of the function just inspected) from the frame. + currentPc = reinterpret_cast(currentStack[2]); + } + // This should not happen. + _LIBCXXABI_TRACE_STATETAB0("skip_non_cxx_eh_aware_frames() reached the end of stack frames, aborting\n"); + abort(); +} + +// __xlc_exception_handle +// This function is for xlclang++. It returns the address of the exception +// object stored in the reserved field in the stack of the caller of the +// function that calls __xlc_exception_handle() (within the link area for the +// call to the latter). The address is stored by the personality routine for +// xlclang++ compiled code. If __xlc_exception_handle() is called by +// non-C++ EH aware functions, their frames are skipped until a C++ EH aware +// frame is found. +// Note: make sure __xlc_excpetion_handle() is a non-leaf function. Currently +// it calls skip_non_cxx_eh_aware_frames(), which in turn calls abort(). +_LIBCXXABI_FUNC_VIS uintptr_t __xlc_exception_handle() { + // Get the SP of this function, i.e., __xlc_exception_handle(). + uintptr_t* lastStack = reinterpret_cast(__builtin_frame_address(0)); + // Move one frame up to the frame of the caller of __xlc_exception_handle(). + lastStack = reinterpret_cast(lastStack[0]); + // Get the return address of this function, i.e., __xlc_exception_handle(). + uint32_t* returnAddress = reinterpret_cast(__builtin_return_address(0)); + + // Skip non-C++ EH aware frames and get the first C++ EH aware frame. + uintptr_t* callerStack = skip_non_cxx_eh_aware_frames(returnAddress, lastStack); + + // Get the SP of the caller of the C++ EH aware caller. + callerStack = reinterpret_cast(callerStack[0]); + // Retrieve the exception object in the stack slot saved by the personality. + uintptr_t exceptionObject = callerStack[3]; + _LIBCXXABI_TRACE_STATETAB("Handshake: retrieve exceptionObject=%p from stack=%p\n", + reinterpret_cast(exceptionObject), reinterpret_cast(callerStack)); + return exceptionObject; +} + +// xlclang++ may generate calls to __Deleted_Virtual. +_LIBCXXABI_FUNC_VIS void __Deleted_Virtual() { abort(); } + +// __catchThrownException is called during AIX library initialization and +// termination to handle exceptions. An implementation is also provided in +// libC.a(shrcore.o). This implementation is provided for applications that +// link with -lc++ (the xlclang++ or ibm-clang++ link default.) +_LIBCXXABI_FUNC_VIS int +__catchThrownException(void (*cdfunc)(void), // function which may fail + void (*cleanup)(void*), // cleanup function + void* cleanuparg, // parameter to cleanup function + int action) { // control exception throwing and termination + enum Action : int { None = 0, Rethrow = 1, Terminate = 2 }; + if (!cdfunc) + return 0; + if (action == Action::Rethrow && !cleanup) { + // No cleanup and rethrow is effectively no-op. + // Avoid the catch handler when possible to allow exceptions generated + // from xlC binaries to flow through. + (*cdfunc)(); + return 0; + } + try { + (*cdfunc)(); + } catch (...) { + if (action == Action::Terminate) + std::terminate(); + if (cleanup) + (*cleanup)(cleanuparg); + if (action == Action::Rethrow) + throw; + assert(action == Action::None); + return -1; // FAILED + } + return 0; +} + +} // extern "C" + +} // __cxxabiv1 diff --git a/third_party/libcxxabi/cxa_aux_runtime.cc b/third_party/libcxxabi/cxa_aux_runtime.cc new file mode 100644 index 000000000..fbe0382d1 --- /dev/null +++ b/third_party/libcxxabi/cxa_aux_runtime.cc @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the "Auxiliary Runtime APIs" +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-aux +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxx/exception" +#include "third_party/libcxx/new" +#include "third_party/libcxx/typeinfo" + +namespace __cxxabiv1 { +extern "C" { +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_cast(); +#else + std::terminate(); +#endif +} + +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_typeid(); +#else + std::terminate(); +#endif +} + +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void +__cxa_throw_bad_array_new_length(void) { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_array_new_length(); +#else + std::terminate(); +#endif +} +} // extern "C" +} // abi diff --git a/third_party/libcxxabi/cxa_default_handlers.cc b/third_party/libcxxabi/cxa_default_handlers.cc new file mode 100644 index 000000000..af2dfd7af --- /dev/null +++ b/third_party/libcxxabi/cxa_default_handlers.cc @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the default terminate_handler, unexpected_handler and +// new_handler. +//===----------------------------------------------------------------------===// + +#include "third_party/libcxx/exception" +#include "third_party/libcxx/memory" +#include "third_party/libcxx/stdlib.h" +#include "third_party/libcxxabi/abort_message.h" +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxxabi/cxa_handlers.h" +#include "third_party/libcxxabi/cxa_exception.h" +#include "third_party/libcxxabi/private_typeinfo.h" +#include "third_party/libcxx/include/atomic_support.hh" // from libc++ + +#if !defined(LIBCXXABI_SILENT_TERMINATE) + +static constinit const char* cause = "uncaught"; + +#ifndef _LIBCXXABI_NO_EXCEPTIONS +// Demangle the given string, or return the string as-is in case of an error. +static std::unique_ptr demangle(char const* str) +{ +#if !defined(LIBCXXABI_NON_DEMANGLING_TERMINATE) + if (const char* result = __cxxabiv1::__cxa_demangle(str, nullptr, nullptr, nullptr)) + return {result, [](char const* p) { std::free(const_cast(p)); }}; +#endif + return {str, [](char const*) { /* nothing to free */ }}; +} + +__attribute__((noreturn)) +static void demangling_terminate_handler() +{ + using namespace __cxxabiv1; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + + // If there is no uncaught exception, just note that we're terminating + if (!globals) + abort_message("terminating"); + + __cxa_exception* exception_header = globals->caughtExceptions; + if (!exception_header) + abort_message("terminating"); + + _Unwind_Exception* unwind_exception = + reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; + + // If we're terminating due to a foreign exception + if (!__isOurExceptionClass(unwind_exception)) + abort_message("terminating due to %s foreign exception", cause); + + void* thrown_object = + __getExceptionClass(unwind_exception) == kOurDependentExceptionClass ? + ((__cxa_dependent_exception*)exception_header)->primaryException : + exception_header + 1; + const __shim_type_info* thrown_type = + static_cast(exception_header->exceptionType); + auto name = demangle(thrown_type->name()); + // If the uncaught exception can be caught with std::exception& + const __shim_type_info* catch_type = + static_cast(&typeid(std::exception)); + if (catch_type->can_catch(thrown_type, thrown_object)) + { + // Include the what() message from the exception + const std::exception* e = static_cast(thrown_object); + abort_message("terminating due to %s exception of type %s: %s", cause, name.get(), e->what()); + } + else + { + // Else just note that we're terminating due to an exception + abort_message("terminating due to %s exception of type %s", cause, name.get()); + } +} +#else // !_LIBCXXABI_NO_EXCEPTIONS +__attribute__((noreturn)) +static void demangling_terminate_handler() +{ + abort_message("terminating"); +} +#endif // !_LIBCXXABI_NO_EXCEPTIONS + +__attribute__((noreturn)) +static void demangling_unexpected_handler() +{ + cause = "unexpected"; + std::terminate(); +} + +static constexpr std::terminate_handler default_terminate_handler = demangling_terminate_handler; +static constexpr std::terminate_handler default_unexpected_handler = demangling_unexpected_handler; +#else // !LIBCXXABI_SILENT_TERMINATE +static constexpr std::terminate_handler default_terminate_handler = ::abort; +static constexpr std::terminate_handler default_unexpected_handler = std::terminate; +#endif // !LIBCXXABI_SILENT_TERMINATE + +// +// Global variables that hold the pointers to the current handler +// +_LIBCXXABI_DATA_VIS +constinit std::terminate_handler __cxa_terminate_handler = default_terminate_handler; + +_LIBCXXABI_DATA_VIS +constinit std::unexpected_handler __cxa_unexpected_handler = default_unexpected_handler; + +_LIBCXXABI_DATA_VIS +constinit std::new_handler __cxa_new_handler = nullptr; + +namespace std +{ + +unexpected_handler +set_unexpected(unexpected_handler func) noexcept +{ + if (func == 0) + func = default_unexpected_handler; + return __libcpp_atomic_exchange(&__cxa_unexpected_handler, func, + _AO_Acq_Rel); +} + +terminate_handler +set_terminate(terminate_handler func) noexcept +{ + if (func == 0) + func = default_terminate_handler; + return __libcpp_atomic_exchange(&__cxa_terminate_handler, func, + _AO_Acq_Rel); +} + +new_handler +set_new_handler(new_handler handler) noexcept +{ + return __libcpp_atomic_exchange(&__cxa_new_handler, handler, _AO_Acq_Rel); +} + +} diff --git a/third_party/libcxxabi/cxa_demangle.cc b/third_party/libcxxabi/cxa_demangle.cc new file mode 100644 index 000000000..8ea592d33 --- /dev/null +++ b/third_party/libcxxabi/cxa_demangle.cc @@ -0,0 +1,409 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// FIXME: (possibly) incomplete list of features that clang mangles that this +// file does not yet support: +// - C++ modules TS + +#include "third_party/libcxxabi/demangle/DemangleConfig.h" +#include "third_party/libcxxabi/demangle/ItaniumDemangle.h" +#include "third_party/libcxxabi/include/__cxxabi_config.h" +#include "third_party/libcxx/cassert" +#include "third_party/libcxx/cctype" +#include "third_party/libcxx/cstdio" +#include "third_party/libcxx/cstdlib" +#include "third_party/libcxx/cstring" +#include "third_party/libcxx/functional" +#include "third_party/libcxx/numeric" +#include "third_party/libcxx/string_view" +#include "third_party/libcxx/utility" + +using namespace itanium_demangle; + +constexpr const char *itanium_demangle::FloatData::spec; +constexpr const char *itanium_demangle::FloatData::spec; +constexpr const char *itanium_demangle::FloatData::spec; + +// := _ # when number < 10 +// := __ _ # when number >= 10 +// extension := decimal-digit+ # at the end of string +const char *itanium_demangle::parse_discriminator(const char *first, + const char *last) { + // parse but ignore discriminator + if (first != last) { + if (*first == '_') { + const char *t1 = first + 1; + if (t1 != last) { + if (std::isdigit(*t1)) + first = t1 + 1; + else if (*t1 == '_') { + for (++t1; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 != last && *t1 == '_') + first = t1 + 1; + } + } + } else if (std::isdigit(*first)) { + const char *t1 = first + 1; + for (; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 == last) + first = last; + } + } + return first; +} + +#ifndef NDEBUG +namespace { +struct DumpVisitor { + unsigned Depth = 0; + bool PendingNewline = false; + + template static constexpr bool wantsNewline(const NodeT *) { + return true; + } + static bool wantsNewline(NodeArray A) { return !A.empty(); } + static constexpr bool wantsNewline(...) { return false; } + + template static bool anyWantNewline(Ts ...Vs) { + for (bool B : {wantsNewline(Vs)...}) + if (B) + return true; + return false; + } + + void printStr(const char *S) { fprintf(stderr, "%s", S); } + void print(std::string_view SV) { + fprintf(stderr, "\"%.*s\"", (int)SV.size(), &*SV.begin()); + } + void print(const Node *N) { + if (N) + N->visit(std::ref(*this)); + else + printStr(""); + } + void print(NodeArray A) { + ++Depth; + printStr("{"); + bool First = true; + for (const Node *N : A) { + if (First) + print(N); + else + printWithComma(N); + First = false; + } + printStr("}"); + --Depth; + } + + // Overload used when T is exactly 'bool', not merely convertible to 'bool'. + void print(bool B) { printStr(B ? "true" : "false"); } + + template + typename std::enable_if::value>::type print(T N) { + fprintf(stderr, "%llu", (unsigned long long)N); + } + + template + typename std::enable_if::value>::type print(T N) { + fprintf(stderr, "%lld", (long long)N); + } + + void print(ReferenceKind RK) { + switch (RK) { + case ReferenceKind::LValue: + return printStr("ReferenceKind::LValue"); + case ReferenceKind::RValue: + return printStr("ReferenceKind::RValue"); + } + } + void print(FunctionRefQual RQ) { + switch (RQ) { + case FunctionRefQual::FrefQualNone: + return printStr("FunctionRefQual::FrefQualNone"); + case FunctionRefQual::FrefQualLValue: + return printStr("FunctionRefQual::FrefQualLValue"); + case FunctionRefQual::FrefQualRValue: + return printStr("FunctionRefQual::FrefQualRValue"); + } + } + void print(Qualifiers Qs) { + if (!Qs) return printStr("QualNone"); + struct QualName { Qualifiers Q; const char *Name; } Names[] = { + {QualConst, "QualConst"}, + {QualVolatile, "QualVolatile"}, + {QualRestrict, "QualRestrict"}, + }; + for (QualName Name : Names) { + if (Qs & Name.Q) { + printStr(Name.Name); + Qs = Qualifiers(Qs & ~Name.Q); + if (Qs) printStr(" | "); + } + } + } + void print(SpecialSubKind SSK) { + switch (SSK) { + case SpecialSubKind::allocator: + return printStr("SpecialSubKind::allocator"); + case SpecialSubKind::basic_string: + return printStr("SpecialSubKind::basic_string"); + case SpecialSubKind::string: + return printStr("SpecialSubKind::string"); + case SpecialSubKind::istream: + return printStr("SpecialSubKind::istream"); + case SpecialSubKind::ostream: + return printStr("SpecialSubKind::ostream"); + case SpecialSubKind::iostream: + return printStr("SpecialSubKind::iostream"); + } + } + void print(TemplateParamKind TPK) { + switch (TPK) { + case TemplateParamKind::Type: + return printStr("TemplateParamKind::Type"); + case TemplateParamKind::NonType: + return printStr("TemplateParamKind::NonType"); + case TemplateParamKind::Template: + return printStr("TemplateParamKind::Template"); + } + } + void print(Node::Prec P) { + switch (P) { + case Node::Prec::Primary: + return printStr("Node::Prec::Primary"); + case Node::Prec::Postfix: + return printStr("Node::Prec::Postfix"); + case Node::Prec::Unary: + return printStr("Node::Prec::Unary"); + case Node::Prec::Cast: + return printStr("Node::Prec::Cast"); + case Node::Prec::PtrMem: + return printStr("Node::Prec::PtrMem"); + case Node::Prec::Multiplicative: + return printStr("Node::Prec::Multiplicative"); + case Node::Prec::Additive: + return printStr("Node::Prec::Additive"); + case Node::Prec::Shift: + return printStr("Node::Prec::Shift"); + case Node::Prec::Spaceship: + return printStr("Node::Prec::Spaceship"); + case Node::Prec::Relational: + return printStr("Node::Prec::Relational"); + case Node::Prec::Equality: + return printStr("Node::Prec::Equality"); + case Node::Prec::And: + return printStr("Node::Prec::And"); + case Node::Prec::Xor: + return printStr("Node::Prec::Xor"); + case Node::Prec::Ior: + return printStr("Node::Prec::Ior"); + case Node::Prec::AndIf: + return printStr("Node::Prec::AndIf"); + case Node::Prec::OrIf: + return printStr("Node::Prec::OrIf"); + case Node::Prec::Conditional: + return printStr("Node::Prec::Conditional"); + case Node::Prec::Assign: + return printStr("Node::Prec::Assign"); + case Node::Prec::Comma: + return printStr("Node::Prec::Comma"); + case Node::Prec::Default: + return printStr("Node::Prec::Default"); + } + } + + void newLine() { + printStr("\n"); + for (unsigned I = 0; I != Depth; ++I) + printStr(" "); + PendingNewline = false; + } + + template void printWithPendingNewline(T V) { + print(V); + if (wantsNewline(V)) + PendingNewline = true; + } + + template void printWithComma(T V) { + if (PendingNewline || wantsNewline(V)) { + printStr(","); + newLine(); + } else { + printStr(", "); + } + + printWithPendingNewline(V); + } + + struct CtorArgPrinter { + DumpVisitor &Visitor; + + template void operator()(T V, Rest ...Vs) { + if (Visitor.anyWantNewline(V, Vs...)) + Visitor.newLine(); + Visitor.printWithPendingNewline(V); + int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 }; + (void)PrintInOrder; + } + }; + + template void operator()(const NodeT *Node) { + Depth += 2; + fprintf(stderr, "%s(", itanium_demangle::NodeKind::name()); + Node->match(CtorArgPrinter{*this}); + fprintf(stderr, ")"); + Depth -= 2; + } + + void operator()(const ForwardTemplateReference *Node) { + Depth += 2; + fprintf(stderr, "ForwardTemplateReference("); + if (Node->Ref && !Node->Printing) { + Node->Printing = true; + CtorArgPrinter{*this}(Node->Ref); + Node->Printing = false; + } else { + CtorArgPrinter{*this}(Node->Index); + } + fprintf(stderr, ")"); + Depth -= 2; + } +}; +} + +void itanium_demangle::Node::dump() const { + DumpVisitor V; + visit(std::ref(V)); + V.newLine(); +} +#endif + +namespace { +class BumpPointerAllocator { + struct BlockMeta { + BlockMeta* Next; + size_t Current; + }; + + static constexpr size_t AllocSize = 4096; + static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); + + alignas(long double) char InitialBuffer[AllocSize]; + BlockMeta* BlockList = nullptr; + + void grow() { + char* NewMeta = static_cast(std::malloc(AllocSize)); + if (NewMeta == nullptr) + std::terminate(); + BlockList = new (NewMeta) BlockMeta{BlockList, 0}; + } + + void* allocateMassive(size_t NBytes) { + NBytes += sizeof(BlockMeta); + BlockMeta* NewMeta = reinterpret_cast(std::malloc(NBytes)); + if (NewMeta == nullptr) + std::terminate(); + BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; + return static_cast(NewMeta + 1); + } + +public: + BumpPointerAllocator() + : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {} + + void* allocate(size_t N) { + N = (N + 15u) & ~15u; + if (N + BlockList->Current >= UsableAllocSize) { + if (N > UsableAllocSize) + return allocateMassive(N); + grow(); + } + BlockList->Current += N; + return static_cast(reinterpret_cast(BlockList + 1) + + BlockList->Current - N); + } + + void reset() { + while (BlockList) { + BlockMeta* Tmp = BlockList; + BlockList = BlockList->Next; + if (reinterpret_cast(Tmp) != InitialBuffer) + std::free(Tmp); + } + BlockList = new (InitialBuffer) BlockMeta{nullptr, 0}; + } + + ~BumpPointerAllocator() { reset(); } +}; + +class DefaultAllocator { + BumpPointerAllocator Alloc; + +public: + void reset() { Alloc.reset(); } + + template T *makeNode(Args &&...args) { + return new (Alloc.allocate(sizeof(T))) + T(std::forward(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.allocate(sizeof(Node *) * sz); + } +}; +} // unnamed namespace + +//===----------------------------------------------------------------------===// +// Code beyond this point should not be synchronized with LLVM. +//===----------------------------------------------------------------------===// + +using Demangler = itanium_demangle::ManglingParser; + +namespace { +enum : int { + demangle_invalid_args = -3, + demangle_invalid_mangled_name = -2, + demangle_memory_alloc_failure = -1, + demangle_success = 0, +}; +} + +namespace __cxxabiv1 { +extern "C" _LIBCXXABI_FUNC_VIS char * +__cxa_demangle(const char *MangledName, char *Buf, size_t *N, int *Status) { + if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { + if (Status) + *Status = demangle_invalid_args; + return nullptr; + } + + int InternalStatus = demangle_success; + Demangler Parser(MangledName, MangledName + std::strlen(MangledName)); + Node *AST = Parser.parse(); + + if (AST == nullptr) + InternalStatus = demangle_invalid_mangled_name; + else { + OutputBuffer O(Buf, N); + assert(Parser.ForwardTemplateRefs.empty()); + AST->print(O); + O += '\0'; + if (N != nullptr) + *N = O.getCurrentPosition(); + Buf = O.getBuffer(); + } + + if (Status) + *Status = InternalStatus; + return InternalStatus == demangle_success ? Buf : nullptr; +} +} // __cxxabiv1 diff --git a/third_party/libcxxabi/cxa_exception.cc b/third_party/libcxxabi/cxa_exception.cc new file mode 100644 index 000000000..1a1cd44bc --- /dev/null +++ b/third_party/libcxxabi/cxa_exception.cc @@ -0,0 +1,776 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the "Exception Handling APIs" +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/include/cxxabi.h" + +#include "third_party/libcxx/exception" // for std::terminate +#include "libc/isystem/string.h" // for memset +#include "third_party/libcxxabi/cxa_exception.h" +#include "third_party/libcxxabi/cxa_handlers.h" +#include "third_party/libcxxabi/fallback_malloc.h" +#include "third_party/libcxx/include/atomic_support.hh" // from libc++ + +#if __has_feature(address_sanitizer) +#include +#endif + +// +---------------------------+-----------------------------+---------------+ +// | __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object | +// +---------------------------+-----------------------------+---------------+ +// ^ +// | +// +-------------------------------------------------------+ +// | +// +---------------------------+-----------------------------+ +// | __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 | +// +---------------------------+-----------------------------+ + +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_exception_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 +size_t cxa_exception_size_from_exception_thrown_size(size_t size) { + return aligned_allocation_size(size + sizeof (__cxa_exception), + alignof(__cxa_exception)); +} + +void __setExceptionClass(_Unwind_Exception* unwind_exception, uint64_t newValue) { + ::memcpy(&unwind_exception->exception_class, &newValue, sizeof(newValue)); +} + + +static void setOurExceptionClass(_Unwind_Exception* unwind_exception) { + __setExceptionClass(unwind_exception, kOurExceptionClass); +} + +static void setDependentExceptionClass(_Unwind_Exception* unwind_exception) { + __setExceptionClass(unwind_exception, kOurDependentExceptionClass); +} + +// Is it one of ours? +uint64_t __getExceptionClass(const _Unwind_Exception* unwind_exception) { + // On x86 and some ARM unwinders, unwind_exception->exception_class is + // a uint64_t. On other ARM unwinders, it is a char[8]. + // See: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf + // So we just copy it into a uint64_t to be sure. + uint64_t exClass; + ::memcpy(&exClass, &unwind_exception->exception_class, sizeof(exClass)); + return exClass; +} + +bool __isOurExceptionClass(const _Unwind_Exception* unwind_exception) { + return (__getExceptionClass(unwind_exception) & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); +} + +static bool isDependentException(_Unwind_Exception* unwind_exception) { + return (__getExceptionClass(unwind_exception) & 0xFF) == 0x01; +} + +// This does not need to be atomic +static inline int incrementHandlerCount(__cxa_exception *exception) { + return ++exception->handlerCount; +} + +// This does not need to be atomic +static inline int decrementHandlerCount(__cxa_exception *exception) { + return --exception->handlerCount; +} + +/* + If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler + stored in exc is called. Otherwise the exceptionDestructor stored in + exc is called, and then the memory for the exception is deallocated. + + This is never called for a __cxa_dependent_exception. +*/ +static +void +exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exception) +{ + __cxa_exception* exception_header = cxa_exception_from_exception_unwind_exception(unwind_exception); + if (_URC_FOREIGN_EXCEPTION_CAUGHT != reason) + std::__terminate(exception_header->terminateHandler); + // Just in case there exists a dependent exception that is pointing to this, + // check the reference count and only destroy this if that count goes to zero. + __cxa_decrement_exception_refcount(unwind_exception + 1); +} + +static _LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) { +// Section 2.5.3 says: +// * For purposes of this ABI, several things are considered exception handlers: +// ** A terminate() call due to a throw. +// and +// * Upon entry, Following initialization of the catch parameter, +// a handler must call: +// * void *__cxa_begin_catch(void *exceptionObject ); + (void) __cxa_begin_catch(&exception_header->unwindHeader); + std::__terminate(exception_header->terminateHandler); +} + +// Return the offset of the __cxa_exception header from the start of the +// allocated buffer. If __cxa_exception's alignment is smaller than the maximum +// useful alignment for the target machine, padding has to be inserted before +// the header to ensure the thrown object that follows the header is +// sufficiently aligned. This happens if _Unwind_exception isn't double-word +// aligned (on Darwin, for example). +static size_t get_cxa_exception_offset() { + struct S { + } __attribute__((aligned)); + + // Compute the maximum alignment for the target machine. + constexpr size_t alignment = alignof(S); + constexpr size_t excp_size = sizeof(__cxa_exception); + constexpr size_t aligned_size = + (excp_size + alignment - 1) / alignment * alignment; + constexpr size_t offset = aligned_size - excp_size; + static_assert((offset == 0 || alignof(_Unwind_Exception) < alignment), + "offset is non-zero only if _Unwind_Exception isn't aligned"); + return offset; +} + +extern "C" { + +// 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) throw() { + size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size); + + // Allocate extra space before the __cxa_exception header to ensure the + // start of the thrown object is sufficiently aligned. + size_t header_offset = get_cxa_exception_offset(); + char *raw_buffer = + (char *)__aligned_malloc_with_fallback(header_offset + actual_size); + if (NULL == raw_buffer) + std::terminate(); + __cxa_exception *exception_header = + static_cast<__cxa_exception *>((void *)(raw_buffer + header_offset)); + ::memset(exception_header, 0, actual_size); + return thrown_object_from_cxa_exception(exception_header); +} + + +// Free a __cxa_exception object allocated with __cxa_allocate_exception. +void __cxa_free_exception(void *thrown_object) throw() { + // Compute the size of the padding before the header. + size_t header_offset = get_cxa_exception_offset(); + char *raw_buffer = + ((char *)cxa_exception_from_thrown_object(thrown_object)) - header_offset; + __aligned_free_with_fallback((void *)raw_buffer); +} + + +// This function shall allocate a __cxa_dependent_exception and +// return a pointer to it. (Really to the object, not past its' end). +// Otherwise, it will work like __cxa_allocate_exception. +void * __cxa_allocate_dependent_exception () { + size_t actual_size = sizeof(__cxa_dependent_exception); + void *ptr = __aligned_malloc_with_fallback(actual_size); + if (NULL == ptr) + std::terminate(); + ::memset(ptr, 0, actual_size); + return ptr; +} + + +// This function shall free a dependent_exception. +// It does not affect the reference count of the primary exception. +void __cxa_free_dependent_exception (void * dependent_exception) { + __aligned_free_with_fallback(dependent_exception); +} + + +// 2.4.3 Throwing the Exception Object +/* +After constructing the exception object with the throw argument value, +the generated code calls the __cxa_throw runtime library routine. This +routine never returns. + +The __cxa_throw routine will do the following: + +* Obtain the __cxa_exception header from the thrown exception object address, +which can be computed as follows: + __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); +* Save the current unexpected_handler and terminate_handler in the __cxa_exception header. +* Save the tinfo and dest arguments in the __cxa_exception header. +* Set the exception_class field in the unwind header. This is a 64-bit value +representing the ASCII string "XXXXC++\0", where "XXXX" is a +vendor-dependent string. That is, for implementations conforming to this +ABI, the low-order 4 bytes of this 64-bit value will be "C++\0". +* Increment the uncaught_exception flag. +* Call _Unwind_RaiseException in the system unwind library, Its argument is the +pointer to the thrown exception, which __cxa_throw itself received as an argument. +__Unwind_RaiseException begins the process of stack unwinding, described +in Section 2.5. In special cases, such as an inability to find a +handler, _Unwind_RaiseException may return. In that case, __cxa_throw +will call terminate, assuming that there was no handler for the +exception. +*/ +void +__cxa_throw(void *thrown_object, std::type_info *tinfo, void (_LIBCXXABI_DTOR_FUNC *dest)(void *)) { + __cxa_eh_globals *globals = __cxa_get_globals(); + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + + exception_header->unexpectedHandler = std::get_unexpected(); + exception_header->terminateHandler = std::get_terminate(); + exception_header->exceptionType = tinfo; + exception_header->exceptionDestructor = dest; + setOurExceptionClass(&exception_header->unwindHeader); + exception_header->referenceCount = 1; // This is a newly allocated exception, no need for thread safety. + globals->uncaughtExceptions += 1; // Not atomically, since globals are thread-local + + exception_header->unwindHeader.exception_cleanup = exception_cleanup_func; + +#if __has_feature(address_sanitizer) + // Inform the ASan runtime that now might be a good time to clean stuff up. + __asan_handle_no_return(); +#endif + +#ifdef __USING_SJLJ_EXCEPTIONS__ + _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); +#else + _Unwind_RaiseException(&exception_header->unwindHeader); +#endif + // This only happens when there is no handler, or some unexpected unwinding + // error happens. + failed_throw(exception_header); +} + + +// 2.5.3 Exception Handlers +/* +The adjusted pointer is computed by the personality routine during phase 1 + and saved in the exception header (either __cxa_exception or + __cxa_dependent_exception). + + Requires: exception is native +*/ +void *__cxa_get_exception_ptr(void *unwind_exception) throw() { +#if defined(_LIBCXXABI_ARM_EHABI) + return reinterpret_cast( + static_cast<_Unwind_Control_Block*>(unwind_exception)->barrier_cache.bitpattern[0]); +#else + return cxa_exception_from_exception_unwind_exception( + static_cast<_Unwind_Exception*>(unwind_exception))->adjustedPtr; +#endif +} + +#if defined(_LIBCXXABI_ARM_EHABI) +/* +The routine to be called before the cleanup. This will save __cxa_exception in +__cxa_eh_globals, so that __cxa_end_cleanup() can recover later. +*/ +bool __cxa_begin_cleanup(void *unwind_arg) throw() { + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception* exception_header = + cxa_exception_from_exception_unwind_exception(unwind_exception); + + if (__isOurExceptionClass(unwind_exception)) + { + if (0 == exception_header->propagationCount) + { + exception_header->nextPropagatingException = globals->propagatingExceptions; + globals->propagatingExceptions = exception_header; + } + ++exception_header->propagationCount; + } + else + { + // If the propagatingExceptions stack is not empty, since we can't + // chain the foreign exception, terminate it. + if (NULL != globals->propagatingExceptions) + std::terminate(); + globals->propagatingExceptions = exception_header; + } + return true; +} + +/* +The routine to be called after the cleanup has been performed. It will get the +propagating __cxa_exception from __cxa_eh_globals, and continue the stack +unwinding with _Unwind_Resume. + +According to ARM EHABI 8.4.1, __cxa_end_cleanup() should not clobber any +register, thus we have to write this function in assembly so that we can save +{r1, r2, r3}. We don't have to save r0 because it is the return value and the +first argument to _Unwind_Resume(). The function also saves/restores r4 to +keep the stack aligned and to provide a temp register. _Unwind_Resume never +returns and we need to keep the original lr so just branch to it. When +targeting bare metal, the function also clobbers ip/r12 to hold the address of +_Unwind_Resume, which may be too far away for an ordinary branch. +*/ +__attribute__((used)) static _Unwind_Exception * +__cxa_end_cleanup_impl() +{ + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception* exception_header = globals->propagatingExceptions; + if (NULL == exception_header) + { + // It seems that __cxa_begin_cleanup() is not called properly. + // We have no choice but terminate the program now. + std::terminate(); + } + + if (__isOurExceptionClass(&exception_header->unwindHeader)) + { + --exception_header->propagationCount; + if (0 == exception_header->propagationCount) + { + globals->propagatingExceptions = exception_header->nextPropagatingException; + exception_header->nextPropagatingException = NULL; + } + } + else + { + globals->propagatingExceptions = NULL; + } + return &exception_header->unwindHeader; +} + +asm(" .pushsection .text.__cxa_end_cleanup,\"ax\",%progbits\n" + " .globl __cxa_end_cleanup\n" + " .type __cxa_end_cleanup,%function\n" + "__cxa_end_cleanup:\n" +#if defined(__ARM_FEATURE_BTI_DEFAULT) + " bti\n" +#endif + " push {r1, r2, r3, r4}\n" + " mov r4, lr\n" + " bl __cxa_end_cleanup_impl\n" + " mov lr, r4\n" +#if defined(LIBCXXABI_BAREMETAL) + " ldr r4, =_Unwind_Resume\n" + " mov ip, r4\n" +#endif + " pop {r1, r2, r3, r4}\n" +#if defined(LIBCXXABI_BAREMETAL) + " bx ip\n" +#else + " b _Unwind_Resume\n" +#endif + " .popsection"); +#endif // defined(_LIBCXXABI_ARM_EHABI) + +/* +This routine can catch foreign or native exceptions. If native, the exception +can be a primary or dependent variety. This routine may remain blissfully +ignorant of whether the native exception is primary or dependent. + +If the exception is native: +* Increment's the exception's handler count. +* Push the exception on the stack of currently-caught exceptions if it is not + already there (from a rethrow). +* Decrements the uncaught_exception count. +* Returns the adjusted pointer to the exception object, which is stored in + the __cxa_exception by the personality routine. + +If the exception is foreign, this means it did not originate from one of throw +routines. The foreign exception does not necessarily have a __cxa_exception +header. However we can catch it here with a catch (...), or with a call +to terminate or unexpected during unwinding. +* Do not try to increment the exception's handler count, we don't know where + it is. +* Push the exception on the stack of currently-caught exceptions only if the + stack is empty. The foreign exception has no way to link to the current + top of stack. If the stack is not empty, call terminate. Even with an + empty stack, this is hacked in by pushing a pointer to an imaginary + __cxa_exception block in front of the foreign exception. It would be better + if the __cxa_eh_globals structure had a stack of _Unwind_Exception, but it + doesn't. It has a stack of __cxa_exception (which has a next* in it). +* Do not decrement the uncaught_exception count because we didn't increment it + in __cxa_throw (or one of our rethrow functions). +* If we haven't terminated, assume the exception object is just past the + _Unwind_Exception and return a pointer to that. +*/ +void* +__cxa_begin_catch(void* unwind_arg) throw() +{ + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); + bool native_exception = __isOurExceptionClass(unwind_exception); + __cxa_eh_globals* globals = __cxa_get_globals(); + // exception_header is a hackish offset from a foreign exception, but it + // works as long as we're careful not to try to access any __cxa_exception + // parts. + __cxa_exception* exception_header = + cxa_exception_from_exception_unwind_exception + ( + static_cast<_Unwind_Exception*>(unwind_exception) + ); + +#if defined(__MVS__) + // Remove the exception object from the linked list of exceptions that the z/OS unwinder + // maintains before adding it to the libc++abi list of caught exceptions. + // The libc++abi will manage the lifetime of the exception from this point forward. + _UnwindZOS_PopException(); +#endif + + if (native_exception) + { + // Increment the handler count, removing the flag about being rethrown + exception_header->handlerCount = exception_header->handlerCount < 0 ? + -exception_header->handlerCount + 1 : exception_header->handlerCount + 1; + // place the exception on the top of the stack if it's not already + // there by a previous rethrow + if (exception_header != globals->caughtExceptions) + { + exception_header->nextException = globals->caughtExceptions; + globals->caughtExceptions = exception_header; + } + globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local +#if defined(_LIBCXXABI_ARM_EHABI) + return reinterpret_cast(exception_header->unwindHeader.barrier_cache.bitpattern[0]); +#else + return exception_header->adjustedPtr; +#endif + } + // Else this is a foreign exception + // If the caughtExceptions stack is not empty, terminate + if (globals->caughtExceptions != 0) + std::terminate(); + // Push the foreign exception on to the stack + globals->caughtExceptions = exception_header; + return unwind_exception + 1; +} + + +/* +Upon exit for any reason, a handler must call: + void __cxa_end_catch (); + +This routine can be called for either a native or foreign exception. +For a native exception: +* Locates the most recently caught exception and decrements its handler count. +* Removes the exception from the caught exception stack, if the handler count goes to zero. +* If the handler count goes down to zero, and the exception was not re-thrown + by throw, it locates the primary exception (which may be the same as the one + it's handling) and decrements its reference count. If that reference count + goes to zero, the function destroys the exception. In any case, if the current + exception is a dependent exception, it destroys that. + +For a foreign exception: +* If it has been rethrown, there is nothing to do. +* Otherwise delete the exception and pop the catch stack to empty. +*/ +void __cxa_end_catch() { + static_assert(sizeof(__cxa_exception) == sizeof(__cxa_dependent_exception), + "sizeof(__cxa_exception) must be equal to " + "sizeof(__cxa_dependent_exception)"); + static_assert(__builtin_offsetof(__cxa_exception, referenceCount) == + __builtin_offsetof(__cxa_dependent_exception, + primaryException), + "the layout of __cxa_exception must match the layout of " + "__cxa_dependent_exception"); + static_assert(__builtin_offsetof(__cxa_exception, handlerCount) == + __builtin_offsetof(__cxa_dependent_exception, handlerCount), + "the layout of __cxa_exception must match the layout of " + "__cxa_dependent_exception"); + __cxa_eh_globals* globals = __cxa_get_globals_fast(); // __cxa_get_globals called in __cxa_begin_catch + __cxa_exception* exception_header = globals->caughtExceptions; + // If we've rethrown a foreign exception, then globals->caughtExceptions + // will have been made an empty stack by __cxa_rethrow() and there is + // nothing more to be done. Do nothing! + if (NULL != exception_header) + { + bool native_exception = __isOurExceptionClass(&exception_header->unwindHeader); + if (native_exception) + { + // This is a native exception + if (exception_header->handlerCount < 0) + { + // The exception has been rethrown by __cxa_rethrow, so don't delete it + if (0 == incrementHandlerCount(exception_header)) + { + // Remove from the chain of uncaught exceptions + globals->caughtExceptions = exception_header->nextException; + // but don't destroy + } + // Keep handlerCount negative in case there are nested catch's + // that need to be told that this exception is rethrown. Don't + // erase this rethrow flag until the exception is recaught. + } + else + { + // The native exception has not been rethrown + if (0 == decrementHandlerCount(exception_header)) + { + // Remove from the chain of uncaught exceptions + globals->caughtExceptions = exception_header->nextException; + // Destroy this exception, being careful to distinguish + // between dependent and primary exceptions + if (isDependentException(&exception_header->unwindHeader)) + { + // Reset exception_header to primaryException and deallocate the dependent exception + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(exception_header); + exception_header = + cxa_exception_from_thrown_object(dep_exception_header->primaryException); + __cxa_free_dependent_exception(dep_exception_header); + } + // Destroy the primary exception only if its referenceCount goes to 0 + // (this decrement must be atomic) + __cxa_decrement_exception_refcount(thrown_object_from_cxa_exception(exception_header)); + } + } + } + else + { + // The foreign exception has not been rethrown. Pop the stack + // and delete it. If there are nested catch's and they try + // to touch a foreign exception in any way, that is undefined + // behavior. They likely can't since the only way to catch + // a foreign exception is with catch (...)! + _Unwind_DeleteException(&globals->caughtExceptions->unwindHeader); + globals->caughtExceptions = 0; + } + } +} + +// Note: exception_header may be masquerading as a __cxa_dependent_exception +// and that's ok. exceptionType is there too. +// However watch out for foreign exceptions. Return null for them. +std::type_info *__cxa_current_exception_type() { +// get the current exception + __cxa_eh_globals *globals = __cxa_get_globals_fast(); + if (NULL == globals) + return NULL; // If there have never been any exceptions, there are none now. + __cxa_exception *exception_header = globals->caughtExceptions; + if (NULL == exception_header) + return NULL; // No current exception + if (!__isOurExceptionClass(&exception_header->unwindHeader)) + return NULL; + return exception_header->exceptionType; +} + +// 2.5.4 Rethrowing Exceptions +/* This routine can rethrow native or foreign exceptions. +If the exception is native: +* marks the exception object on top of the caughtExceptions stack + (in an implementation-defined way) as being rethrown. +* If the caughtExceptions stack is empty, it calls terminate() + (see [C++FDIS] [except.throw], 15.1.8). +* It then calls _Unwind_RaiseException which should not return + (terminate if it does). + Note: exception_header may be masquerading as a __cxa_dependent_exception + and that's ok. +*/ +void __cxa_rethrow() { + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception* exception_header = globals->caughtExceptions; + if (NULL == exception_header) + std::terminate(); // throw; called outside of a exception handler + bool native_exception = __isOurExceptionClass(&exception_header->unwindHeader); + if (native_exception) + { + // Mark the exception as being rethrown (reverse the effects of __cxa_begin_catch) + exception_header->handlerCount = -exception_header->handlerCount; + globals->uncaughtExceptions += 1; + // __cxa_end_catch will remove this exception from the caughtExceptions stack if necessary + } + else // this is a foreign exception + { + // The only way to communicate to __cxa_end_catch that we've rethrown + // a foreign exception, so don't delete us, is to pop the stack here + // which must be empty afterwards. Then __cxa_end_catch will do + // nothing + globals->caughtExceptions = 0; + } +#ifdef __USING_SJLJ_EXCEPTIONS__ + _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); +#else + _Unwind_RaiseException(&exception_header->unwindHeader); +#endif + + // If we get here, some kind of unwinding error has occurred. + // There is some weird code generation bug happening with + // Apple clang version 4.0 (tags/Apple/clang-418.0.2) (based on LLVM 3.1svn) + // If we call failed_throw here. Turns up with -O2 or higher, and -Os. + __cxa_begin_catch(&exception_header->unwindHeader); + if (native_exception) + std::__terminate(exception_header->terminateHandler); + // Foreign exception: can't get exception_header->terminateHandler + std::terminate(); +} + +/* + 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) throw() { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1)); + } +} + +/* + 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) throw() { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + if (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0) + { + if (NULL != exception_header->exceptionDestructor) + exception_header->exceptionDestructor(thrown_object); + __cxa_free_exception(thrown_object); + } + } +} + +/* + Returns a pointer to the thrown object (if any) at the top of the + caughtExceptions stack. Atomically increment the exception's referenceCount. + If there is no such thrown object or if the thrown object is foreign, + returns null. + + We can use __cxa_get_globals_fast here to get the globals because if there have + been no exceptions thrown, ever, on this thread, we can return NULL without + the need to allocate the exception-handling globals. +*/ +void *__cxa_current_primary_exception() throw() { +// get the current exception + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (NULL == globals) + return NULL; // If there are no globals, there is no exception + __cxa_exception* exception_header = globals->caughtExceptions; + if (NULL == exception_header) + return NULL; // No current exception + if (!__isOurExceptionClass(&exception_header->unwindHeader)) + return NULL; // Can't capture a foreign exception (no way to refcount it) + if (isDependentException(&exception_header->unwindHeader)) { + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(exception_header); + exception_header = cxa_exception_from_thrown_object(dep_exception_header->primaryException); + } + void* thrown_object = thrown_object_from_cxa_exception(exception_header); + __cxa_increment_exception_refcount(thrown_object); + return thrown_object; +} + +/* + If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler + stored in exc is called. Otherwise the referenceCount stored in the + primary exception is decremented, destroying the primary if necessary. + Finally the dependent exception is destroyed. +*/ +static +void +dependent_exception_cleanup(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exception) +{ + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(unwind_exception + 1) - 1; + if (_URC_FOREIGN_EXCEPTION_CAUGHT != reason) + std::__terminate(dep_exception_header->terminateHandler); + __cxa_decrement_exception_refcount(dep_exception_header->primaryException); + __cxa_free_dependent_exception(dep_exception_header); +} + +/* + If thrown_object is not null, allocate, initialize and throw a dependent + exception. +*/ +void +__cxa_rethrow_primary_exception(void* thrown_object) +{ + if ( thrown_object != NULL ) + { + // thrown_object guaranteed to be native because + // __cxa_current_primary_exception returns NULL for foreign exceptions + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + __cxa_dependent_exception* dep_exception_header = + static_cast<__cxa_dependent_exception*>(__cxa_allocate_dependent_exception()); + dep_exception_header->primaryException = thrown_object; + __cxa_increment_exception_refcount(thrown_object); + dep_exception_header->exceptionType = exception_header->exceptionType; + dep_exception_header->unexpectedHandler = std::get_unexpected(); + dep_exception_header->terminateHandler = std::get_terminate(); + setDependentExceptionClass(&dep_exception_header->unwindHeader); + __cxa_get_globals()->uncaughtExceptions += 1; + dep_exception_header->unwindHeader.exception_cleanup = dependent_exception_cleanup; +#ifdef __USING_SJLJ_EXCEPTIONS__ + _Unwind_SjLj_RaiseException(&dep_exception_header->unwindHeader); +#else + _Unwind_RaiseException(&dep_exception_header->unwindHeader); +#endif + // Some sort of unwinding error. Note that terminate is a handler. + __cxa_begin_catch(&dep_exception_header->unwindHeader); + } + // If we return client will call terminate() +} + +bool +__cxa_uncaught_exception() throw() { return __cxa_uncaught_exceptions() != 0; } + +unsigned int +__cxa_uncaught_exceptions() throw() +{ + // This does not report foreign exceptions in flight + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals == 0) + return 0; + return globals->uncaughtExceptions; +} + +} // extern "C" + +} // abi diff --git a/third_party/libcxxabi/cxa_exception.h b/third_party/libcxxabi/cxa_exception.h new file mode 100644 index 000000000..69386792f --- /dev/null +++ b/third_party/libcxxabi/cxa_exception.h @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the "Exception Handling APIs" +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#ifndef _CXA_EXCEPTION_H +#define _CXA_EXCEPTION_H + +#include "third_party/libcxx/exception" // for std::unexpected_handler and std::terminate_handler +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libunwind/include/unwind.h" + +namespace __cxxabiv1 { + +static const uint64_t kOurExceptionClass = 0x434C4E47432B2B00; // CLNGC++\0 +static const uint64_t kOurDependentExceptionClass = 0x434C4E47432B2B01; // CLNGC++\1 +static const uint64_t get_vendor_and_language = 0xFFFFFFFFFFFFFF00; // mask for CLNGC++ + +_LIBCXXABI_HIDDEN uint64_t __getExceptionClass (const _Unwind_Exception*); +_LIBCXXABI_HIDDEN void __setExceptionClass ( _Unwind_Exception*, uint64_t); +_LIBCXXABI_HIDDEN bool __isOurExceptionClass(const _Unwind_Exception*); + +struct _LIBCXXABI_HIDDEN __cxa_exception { +#if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI) + // Now _Unwind_Exception is marked with __attribute__((aligned)), + // which implies __cxa_exception is also aligned. Insert padding + // in the beginning of the struct, rather than before unwindHeader. + void *reserve; + + // This is a new field to support C++11 exception_ptr. + // For binary compatibility it is at the start of this + // struct which is prepended to the object thrown in + // __cxa_allocate_exception. + size_t referenceCount; +#endif + + // Manage the exception object itself. + std::type_info *exceptionType; + void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + + __cxa_exception *nextException; + + int handlerCount; + +#if defined(_LIBCXXABI_ARM_EHABI) + __cxa_exception* nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + void *catchTemp; + void *adjustedPtr; +#endif + +#if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) + // This is a new field to support C++11 exception_ptr. + // For binary compatibility it is placed where the compiler + // previously added padding to 64-bit align unwindHeader. + size_t referenceCount; +#endif + _Unwind_Exception unwindHeader; +}; + +// http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html +// The layout of this structure MUST match the layout of __cxa_exception, with +// primaryException instead of referenceCount. +struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { +#if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI) + void* reserve; // padding. + void* primaryException; +#endif + + std::type_info *exceptionType; + void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + + __cxa_exception *nextException; + + int handlerCount; + +#if defined(_LIBCXXABI_ARM_EHABI) + __cxa_exception* nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + void * catchTemp; + void *adjustedPtr; +#endif + +#if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) + void* primaryException; +#endif + _Unwind_Exception unwindHeader; +}; + +// Verify the negative offsets of different fields. +static_assert(sizeof(_Unwind_Exception) + + offsetof(__cxa_exception, unwindHeader) == + sizeof(__cxa_exception), + "unwindHeader has wrong negative offsets"); +static_assert(sizeof(_Unwind_Exception) + + offsetof(__cxa_dependent_exception, unwindHeader) == + sizeof(__cxa_dependent_exception), + "unwindHeader has wrong negative offsets"); + +#if defined(_LIBCXXABI_ARM_EHABI) +static_assert(offsetof(__cxa_exception, propagationCount) + + sizeof(_Unwind_Exception) + sizeof(void*) == + sizeof(__cxa_exception), + "propagationCount has wrong negative offset"); +static_assert(offsetof(__cxa_dependent_exception, propagationCount) + + sizeof(_Unwind_Exception) + sizeof(void*) == + sizeof(__cxa_dependent_exception), + "propagationCount has wrong negative offset"); +#elif defined(__LP64__) || defined(_WIN64) +static_assert(offsetof(__cxa_exception, adjustedPtr) + + sizeof(_Unwind_Exception) + sizeof(void*) == + sizeof(__cxa_exception), + "adjustedPtr has wrong negative offset"); +static_assert(offsetof(__cxa_dependent_exception, adjustedPtr) + + sizeof(_Unwind_Exception) + sizeof(void*) == + sizeof(__cxa_dependent_exception), + "adjustedPtr has wrong negative offset"); +#else +static_assert(offsetof(__cxa_exception, referenceCount) + + sizeof(_Unwind_Exception) + sizeof(void*) == + sizeof(__cxa_exception), + "referenceCount has wrong negative offset"); +static_assert(offsetof(__cxa_dependent_exception, primaryException) + + sizeof(_Unwind_Exception) + sizeof(void*) == + sizeof(__cxa_dependent_exception), + "primaryException has wrong negative offset"); +#endif + +struct _LIBCXXABI_HIDDEN __cxa_eh_globals { + __cxa_exception * caughtExceptions; + unsigned int uncaughtExceptions; +#if defined(_LIBCXXABI_ARM_EHABI) + __cxa_exception* propagatingExceptions; +#endif +}; + +extern "C" _LIBCXXABI_FUNC_VIS __cxa_eh_globals * __cxa_get_globals (); +extern "C" _LIBCXXABI_FUNC_VIS __cxa_eh_globals * __cxa_get_globals_fast (); + +extern "C" _LIBCXXABI_FUNC_VIS void * __cxa_allocate_dependent_exception (); +extern "C" _LIBCXXABI_FUNC_VIS void __cxa_free_dependent_exception (void * dependent_exception); + +} // namespace __cxxabiv1 + +#endif // _CXA_EXCEPTION_H diff --git a/third_party/libcxxabi/cxa_exception_storage.cc b/third_party/libcxxabi/cxa_exception_storage.cc new file mode 100644 index 000000000..cff0dedf0 --- /dev/null +++ b/third_party/libcxxabi/cxa_exception_storage.cc @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the storage for the "Caught Exception Stack" +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-exc-stack +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/cxa_exception.h" + +#include "third_party/libcxx/__threading_support" + +#if defined(_LIBCXXABI_HAS_NO_THREADS) + +namespace __cxxabiv1 { +extern "C" { + static __cxa_eh_globals eh_globals; + __cxa_eh_globals *__cxa_get_globals() { return &eh_globals; } + __cxa_eh_globals *__cxa_get_globals_fast() { return &eh_globals; } +} // extern "C" +} // namespace __cxxabiv1 + +#elif defined(HAS_THREAD_LOCAL) + +namespace __cxxabiv1 { +namespace { + __cxa_eh_globals *__globals() { + static thread_local __cxa_eh_globals eh_globals; + return &eh_globals; + } +} // namespace + +extern "C" { + __cxa_eh_globals *__cxa_get_globals() { return __globals(); } + __cxa_eh_globals *__cxa_get_globals_fast() { return __globals(); } +} // extern "C" +} // namespace __cxxabiv1 + +#else + +#include "third_party/libcxxabi/abort_message.h" +#include "third_party/libcxxabi/fallback_malloc.h" + +#if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB) +#pragma comment(lib, "pthread") +#endif + +// In general, we treat all threading errors as fatal. +// We cannot call std::terminate() because that will in turn +// call __cxa_get_globals() and cause infinite recursion. + +namespace __cxxabiv1 { +namespace { + std::__libcpp_tls_key key_; + std::__libcpp_exec_once_flag flag_ = _LIBCPP_EXEC_ONCE_INITIALIZER; + + void _LIBCPP_TLS_DESTRUCTOR_CC destruct_(void *p) { + __free_with_fallback(p); + if (0 != std::__libcpp_tls_set(key_, NULL)) + abort_message("cannot zero out thread value for __cxa_get_globals()"); + } + + void construct_() { + if (0 != std::__libcpp_tls_create(&key_, destruct_)) + abort_message("cannot create thread specific key for __cxa_get_globals()"); + } +} // namespace + +extern "C" { + __cxa_eh_globals *__cxa_get_globals() { + // Try to get the globals for this thread + __cxa_eh_globals *retVal = __cxa_get_globals_fast(); + + // If this is the first time we've been asked for these globals, create them + if (NULL == retVal) { + retVal = static_cast<__cxa_eh_globals*>( + __calloc_with_fallback(1, sizeof(__cxa_eh_globals))); + if (NULL == retVal) + abort_message("cannot allocate __cxa_eh_globals"); + if (0 != std::__libcpp_tls_set(key_, retVal)) + abort_message("std::__libcpp_tls_set failure in __cxa_get_globals()"); + } + return retVal; + } + + // Note that this implementation will reliably return NULL if not + // preceded by a call to __cxa_get_globals(). This is an extension + // to the Itanium ABI and is taken advantage of in several places in + // libc++abi. + __cxa_eh_globals *__cxa_get_globals_fast() { + // First time through, create the key. + if (0 != std::__libcpp_execute_once(&flag_, construct_)) + abort_message("execute once failure in __cxa_get_globals_fast()"); + return static_cast<__cxa_eh_globals*>(std::__libcpp_tls_get(key_)); + } +} // extern "C" +} // namespace __cxxabiv1 + +#endif diff --git a/third_party/libcxxabi/cxa_guard.cc b/third_party/libcxxabi/cxa_guard.cc new file mode 100644 index 000000000..7f89c4939 --- /dev/null +++ b/third_party/libcxxabi/cxa_guard.cc @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/include/__cxxabi_config.h" +#include "third_party/libcxxabi/include/cxxabi.h" + +// Tell the implementation that we're building the actual implementation +// (and not testing it) +#define BUILDING_CXA_GUARD +#include "third_party/libcxxabi/cxa_guard_impl.h" + +/* + This implementation must be careful to not call code external to this file + which will turn around and try to call __cxa_guard_acquire reentrantly. + For this reason, the headers of this file are as restricted as possible. + Previous implementations of this code for __APPLE__ have used + std::__libcpp_mutex_lock and the abort_message utility without problem. This + implementation also uses std::__libcpp_condvar_wait which has tested + to not be a problem. +*/ + +namespace __cxxabiv1 { + +#if defined(_LIBCXXABI_GUARD_ABI_ARM) +using guard_type = uint32_t; +#else +using guard_type = uint64_t; +#endif + +extern "C" +{ +_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type* raw_guard_object) { + SelectedImplementation imp(raw_guard_object); + return static_cast(imp.cxa_guard_acquire()); +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *raw_guard_object) { + SelectedImplementation imp(raw_guard_object); + imp.cxa_guard_release(); +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *raw_guard_object) { + SelectedImplementation imp(raw_guard_object); + imp.cxa_guard_abort(); +} +} // extern "C" + +} // __cxxabiv1 diff --git a/third_party/libcxxabi/cxa_guard_impl.h b/third_party/libcxxabi/cxa_guard_impl.h new file mode 100644 index 000000000..4b9295268 --- /dev/null +++ b/third_party/libcxxabi/cxa_guard_impl.h @@ -0,0 +1,674 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H +#define LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H + +/* cxa_guard_impl.h - Implements the C++ runtime support for function local + * static guards. + * The layout of the guard object is the same across ARM and Itanium. + * + * The first "guard byte" (which is checked by the compiler) is set only upon + * the completion of cxa release. + * + * The second "init byte" does the rest of the bookkeeping. It tracks if + * initialization is complete or pending, and if there are waiting threads. + * + * If the guard variable is 64-bits and the platforms supplies a 32-bit thread + * identifier, it is used to detect recursive initialization. The thread ID of + * the thread currently performing initialization is stored in the second word. + * + * Guard Object Layout: + * --------------------------------------------------------------------------- + * | a+0: guard byte | a+1: init byte | a+2: unused ... | a+4: thread-id ... | + * --------------------------------------------------------------------------- + * + * Note that we don't do what the ABI docs suggest (put a mutex in the guard + * object which we acquire in cxa_guard_acquire and release in + * cxa_guard_release). Instead we use the init byte to imitate that behaviour, + * but without actually holding anything mutex related between aquire and + * release/abort. + * + * Access Protocol: + * For each implementation the guard byte is checked and set before accessing + * the init byte. + * + * Overall Design: + * The implementation was designed to allow each implementation to be tested + * independent of the C++ runtime or platform support. + * + */ + +#include "third_party/libcxxabi/include/__cxxabi_config.h" +#include "third_party/libcxx/include/atomic_support.hh" // from libc++ +#if defined(__has_include) +# if __has_include() +# include +# endif +# if __has_include() +# include +# endif +#endif + +#include "third_party/libcxx/__threading_support" +#include "third_party/libcxx/cstdint" +#include "third_party/libcxx/cstring" +#include "libc/isystem/limits.h" +#include "libc/isystem/stdlib.h" + +#ifndef _LIBCXXABI_HAS_NO_THREADS +# if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB) +# pragma comment(lib, "pthread") +# endif +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" +#endif + +// To make testing possible, this header is included from both cxa_guard.cpp +// and a number of tests. +// +// For this reason we place everything in an anonymous namespace -- even though +// we're in a header. We want the actual implementation and the tests to have +// unique definitions of the types in this header (since the tests may depend +// on function local statics). +// +// To enforce this either `BUILDING_CXA_GUARD` or `TESTING_CXA_GUARD` must be +// defined when including this file. Only `src/cxa_guard.cpp` should define +// the former. +#ifdef BUILDING_CXA_GUARD +# include "third_party/libcxxabi/abort_message.h" +# define ABORT_WITH_MESSAGE(...) ::abort_message(__VA_ARGS__) +#elif defined(TESTING_CXA_GUARD) +# define ABORT_WITH_MESSAGE(...) ::abort() +#else +# error "Either BUILDING_CXA_GUARD or TESTING_CXA_GUARD must be defined" +#endif + +#if __has_feature(thread_sanitizer) +extern "C" void __tsan_acquire(void*); +extern "C" void __tsan_release(void*); +#else +# define __tsan_acquire(addr) ((void)0) +# define __tsan_release(addr) ((void)0) +#endif + +namespace __cxxabiv1 { +// Use an anonymous namespace to ensure that the tests and actual implementation +// have unique definitions of these symbols. +namespace { + +//===----------------------------------------------------------------------===// +// Misc Utilities +//===----------------------------------------------------------------------===// + +template +struct LazyValue { + LazyValue() : is_init(false) {} + + T& get() { + if (!is_init) { + value = Init(); + is_init = true; + } + return value; + } + +private: + T value; + bool is_init = false; +}; + +template +class AtomicInt { +public: + using MemoryOrder = std::__libcpp_atomic_order; + + explicit AtomicInt(IntType* b) : b_(b) {} + AtomicInt(AtomicInt const&) = delete; + AtomicInt& operator=(AtomicInt const&) = delete; + + IntType load(MemoryOrder ord) { return std::__libcpp_atomic_load(b_, ord); } + void store(IntType val, MemoryOrder ord) { std::__libcpp_atomic_store(b_, val, ord); } + IntType exchange(IntType new_val, MemoryOrder ord) { return std::__libcpp_atomic_exchange(b_, new_val, ord); } + bool compare_exchange(IntType* expected, IntType desired, MemoryOrder ord_success, MemoryOrder ord_failure) { + return std::__libcpp_atomic_compare_exchange(b_, expected, desired, ord_success, ord_failure); + } + +private: + IntType* b_; +}; + +//===----------------------------------------------------------------------===// +// PlatformGetThreadID +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD) +uint32_t PlatformThreadID() { + static_assert(sizeof(mach_port_t) == sizeof(uint32_t), ""); + return static_cast(pthread_mach_thread_np(std::__libcpp_thread_get_current_id())); +} +#elif defined(SYS_gettid) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD) +uint32_t PlatformThreadID() { + static_assert(sizeof(pid_t) == sizeof(uint32_t), ""); + return static_cast(syscall(SYS_gettid)); +} +#else +constexpr uint32_t (*PlatformThreadID)() = nullptr; +#endif + +//===----------------------------------------------------------------------===// +// GuardByte +//===----------------------------------------------------------------------===// + +static constexpr uint8_t UNSET = 0; +static constexpr uint8_t COMPLETE_BIT = (1 << 0); +static constexpr uint8_t PENDING_BIT = (1 << 1); +static constexpr uint8_t WAITING_BIT = (1 << 2); + +/// Manages reads and writes to the guard byte. +struct GuardByte { + GuardByte() = delete; + GuardByte(GuardByte const&) = delete; + GuardByte& operator=(GuardByte const&) = delete; + + explicit GuardByte(uint8_t* const guard_byte_address) : guard_byte(guard_byte_address) {} + +public: + /// The guard byte portion of cxa_guard_acquire. Returns true if + /// initialization has already been completed. + bool acquire() { + // if guard_byte is non-zero, we have already completed initialization + // (i.e. release has been called) + return guard_byte.load(std::_AO_Acquire) != UNSET; + } + + /// The guard byte portion of cxa_guard_release. + void release() { guard_byte.store(COMPLETE_BIT, std::_AO_Release); } + + /// The guard byte portion of cxa_guard_abort. + void abort() {} // Nothing to do + +private: + AtomicInt guard_byte; +}; + +//===----------------------------------------------------------------------===// +// InitByte Implementations +//===----------------------------------------------------------------------===// +// +// Each initialization byte implementation supports the following methods: +// +// InitByte(uint8_t* _init_byte_address, uint32_t* _thread_id_address) +// Construct the InitByte object, initializing our member variables +// +// bool acquire() +// Called before we start the initialization. Check if someone else has already started, and if +// not to signal our intent to start it ourselves. We determine the current status from the init +// byte, which is one of 4 possible values: +// COMPLETE: Initialization was finished by somebody else. Return true. +// PENDING: Somebody has started the initialization already, set the WAITING bit, +// then wait for the init byte to get updated with a new value. +// (PENDING|WAITING): Somebody has started the initialization already, and we're not the +// first one waiting. Wait for the init byte to get updated. +// UNSET: Initialization hasn't successfully completed, and nobody is currently +// performing the initialization. Set the PENDING bit to indicate our +// intention to start the initialization, and return false. +// The return value indicates whether initialization has already been completed. +// +// void release() +// Called after successfully completing the initialization. Update the init byte to reflect +// that, then if anybody else is waiting, wake them up. +// +// void abort() +// Called after an error is thrown during the initialization. Reset the init byte to UNSET to +// indicate that we're no longer performing the initialization, then if anybody is waiting, wake +// them up so they can try performing the initialization. +// + +//===----------------------------------------------------------------------===// +// Single Threaded Implementation +//===----------------------------------------------------------------------===// + +/// InitByteNoThreads - Doesn't use any inter-thread synchronization when +/// managing reads and writes to the init byte. +struct InitByteNoThreads { + InitByteNoThreads() = delete; + InitByteNoThreads(InitByteNoThreads const&) = delete; + InitByteNoThreads& operator=(InitByteNoThreads const&) = delete; + + explicit InitByteNoThreads(uint8_t* _init_byte_address, uint32_t*) : init_byte_address(_init_byte_address) {} + + /// The init byte portion of cxa_guard_acquire. Returns true if + /// initialization has already been completed. + bool acquire() { + if (*init_byte_address == COMPLETE_BIT) + return true; + if (*init_byte_address & PENDING_BIT) + ABORT_WITH_MESSAGE("__cxa_guard_acquire detected recursive initialization: do you have a function-local static variable whose initialization depends on that function?"); + *init_byte_address = PENDING_BIT; + return false; + } + + /// The init byte portion of cxa_guard_release. + void release() { *init_byte_address = COMPLETE_BIT; } + /// The init byte portion of cxa_guard_abort. + void abort() { *init_byte_address = UNSET; } + +private: + /// The address of the byte used during initialization. + uint8_t* const init_byte_address; +}; + +//===----------------------------------------------------------------------===// +// Global Mutex Implementation +//===----------------------------------------------------------------------===// + +struct LibcppMutex; +struct LibcppCondVar; + +#ifndef _LIBCXXABI_HAS_NO_THREADS +struct LibcppMutex { + LibcppMutex() = default; + LibcppMutex(LibcppMutex const&) = delete; + LibcppMutex& operator=(LibcppMutex const&) = delete; + + bool lock() { return std::__libcpp_mutex_lock(&mutex); } + bool unlock() { return std::__libcpp_mutex_unlock(&mutex); } + +private: + friend struct LibcppCondVar; + std::__libcpp_mutex_t mutex = _LIBCPP_MUTEX_INITIALIZER; +}; + +struct LibcppCondVar { + LibcppCondVar() = default; + LibcppCondVar(LibcppCondVar const&) = delete; + LibcppCondVar& operator=(LibcppCondVar const&) = delete; + + bool wait(LibcppMutex& mut) { return std::__libcpp_condvar_wait(&cond, &mut.mutex); } + bool broadcast() { return std::__libcpp_condvar_broadcast(&cond); } + +private: + std::__libcpp_condvar_t cond = _LIBCPP_CONDVAR_INITIALIZER; +}; +#else +struct LibcppMutex {}; +struct LibcppCondVar {}; +#endif // !defined(_LIBCXXABI_HAS_NO_THREADS) + +/// InitByteGlobalMutex - Uses a global mutex and condition variable (common to +/// all static local variables) to manage reads and writes to the init byte. +template +struct InitByteGlobalMutex { + + explicit InitByteGlobalMutex(uint8_t* _init_byte_address, uint32_t* _thread_id_address) + : init_byte_address(_init_byte_address), thread_id_address(_thread_id_address), + has_thread_id_support(_thread_id_address != nullptr && GetThreadID != nullptr) {} + +public: + /// The init byte portion of cxa_guard_acquire. Returns true if + /// initialization has already been completed. + bool acquire() { + LockGuard g("__cxa_guard_acquire"); + // Check for possible recursive initialization. + if (has_thread_id_support && (*init_byte_address & PENDING_BIT)) { + if (*thread_id_address == current_thread_id.get()) + ABORT_WITH_MESSAGE("__cxa_guard_acquire detected recursive initialization: do you have a function-local static variable whose initialization depends on that function?"); + } + + // Wait until the pending bit is not set. + while (*init_byte_address & PENDING_BIT) { + *init_byte_address |= WAITING_BIT; + global_cond.wait(global_mutex); + } + + if (*init_byte_address == COMPLETE_BIT) + return true; + + if (has_thread_id_support) + *thread_id_address = current_thread_id.get(); + + *init_byte_address = PENDING_BIT; + return false; + } + + /// The init byte portion of cxa_guard_release. + void release() { + bool has_waiting; + { + LockGuard g("__cxa_guard_release"); + has_waiting = *init_byte_address & WAITING_BIT; + *init_byte_address = COMPLETE_BIT; + } + if (has_waiting) { + if (global_cond.broadcast()) { + ABORT_WITH_MESSAGE("%s failed to broadcast", "__cxa_guard_release"); + } + } + } + + /// The init byte portion of cxa_guard_abort. + void abort() { + bool has_waiting; + { + LockGuard g("__cxa_guard_abort"); + if (has_thread_id_support) + *thread_id_address = 0; + has_waiting = *init_byte_address & WAITING_BIT; + *init_byte_address = UNSET; + } + if (has_waiting) { + if (global_cond.broadcast()) { + ABORT_WITH_MESSAGE("%s failed to broadcast", "__cxa_guard_abort"); + } + } + } + +private: + /// The address of the byte used during initialization. + uint8_t* const init_byte_address; + /// An optional address storing an identifier for the thread performing initialization. + /// It's used to detect recursive initialization. + uint32_t* const thread_id_address; + + const bool has_thread_id_support; + LazyValue current_thread_id; + +private: + struct LockGuard { + LockGuard() = delete; + LockGuard(LockGuard const&) = delete; + LockGuard& operator=(LockGuard const&) = delete; + + explicit LockGuard(const char* calling_func) : calling_func_(calling_func) { + if (global_mutex.lock()) + ABORT_WITH_MESSAGE("%s failed to acquire mutex", calling_func_); + } + + ~LockGuard() { + if (global_mutex.unlock()) + ABORT_WITH_MESSAGE("%s failed to release mutex", calling_func_); + } + + private: + const char* const calling_func_; + }; +}; + +//===----------------------------------------------------------------------===// +// Futex Implementation +//===----------------------------------------------------------------------===// + +#if defined(SYS_futex) +void PlatformFutexWait(int* addr, int expect) { + constexpr int WAIT = 0; + syscall(SYS_futex, addr, WAIT, expect, 0); + __tsan_acquire(addr); +} +void PlatformFutexWake(int* addr) { + constexpr int WAKE = 1; + __tsan_release(addr); + syscall(SYS_futex, addr, WAKE, INT_MAX); +} +#else +constexpr void (*PlatformFutexWait)(int*, int) = nullptr; +constexpr void (*PlatformFutexWake)(int*) = nullptr; +#endif + +constexpr bool PlatformSupportsFutex() { return +PlatformFutexWait != nullptr; } + +/// InitByteFutex - Uses a futex to manage reads and writes to the init byte. +template +struct InitByteFutex { + + explicit InitByteFutex(uint8_t* _init_byte_address, uint32_t* _thread_id_address) + : init_byte(_init_byte_address), + has_thread_id_support(_thread_id_address != nullptr && GetThreadIDArg != nullptr), + thread_id(_thread_id_address), + base_address(reinterpret_cast(/*_init_byte_address & ~0x3*/ _init_byte_address - 1)) {} + +public: + /// The init byte portion of cxa_guard_acquire. Returns true if + /// initialization has already been completed. + bool acquire() { + while (true) { + uint8_t last_val = UNSET; + if (init_byte.compare_exchange(&last_val, PENDING_BIT, std::_AO_Acq_Rel, std::_AO_Acquire)) { + if (has_thread_id_support) { + thread_id.store(current_thread_id.get(), std::_AO_Relaxed); + } + return false; + } + + if (last_val == COMPLETE_BIT) + return true; + + if (last_val & PENDING_BIT) { + + // Check for recursive initialization + if (has_thread_id_support && thread_id.load(std::_AO_Relaxed) == current_thread_id.get()) { + ABORT_WITH_MESSAGE("__cxa_guard_acquire detected recursive initialization: do you have a function-local static variable whose initialization depends on that function?"); + } + + if ((last_val & WAITING_BIT) == 0) { + // This compare exchange can fail for several reasons + // (1) another thread finished the whole thing before we got here + // (2) another thread set the waiting bit we were trying to thread + // (3) another thread had an exception and failed to finish + if (!init_byte.compare_exchange(&last_val, PENDING_BIT | WAITING_BIT, std::_AO_Acq_Rel, std::_AO_Release)) { + // (1) success, via someone else's work! + if (last_val == COMPLETE_BIT) + return true; + + // (3) someone else, bailed on doing the work, retry from the start! + if (last_val == UNSET) + continue; + + // (2) the waiting bit got set, so we are happy to keep waiting + } + } + wait_on_initialization(); + } + } + } + + /// The init byte portion of cxa_guard_release. + void release() { + uint8_t old = init_byte.exchange(COMPLETE_BIT, std::_AO_Acq_Rel); + if (old & WAITING_BIT) + wake_all(); + } + + /// The init byte portion of cxa_guard_abort. + void abort() { + if (has_thread_id_support) + thread_id.store(0, std::_AO_Relaxed); + + uint8_t old = init_byte.exchange(UNSET, std::_AO_Acq_Rel); + if (old & WAITING_BIT) + wake_all(); + } + +private: + /// Use the futex to wait on the current guard variable. Futex expects a + /// 32-bit 4-byte aligned address as the first argument, so we use the 4-byte + /// aligned address that encompasses the init byte (i.e. the address of the + /// raw guard object that was passed to __cxa_guard_acquire/release/abort). + void wait_on_initialization() { Wait(base_address, expected_value_for_futex(PENDING_BIT | WAITING_BIT)); } + void wake_all() { Wake(base_address); } + +private: + AtomicInt init_byte; + + const bool has_thread_id_support; + // Unsafe to use unless has_thread_id_support + AtomicInt thread_id; + LazyValue current_thread_id; + + /// the 4-byte-aligned address that encompasses the init byte (i.e. the + /// address of the raw guard object). + int* const base_address; + + /// Create the expected integer value for futex `wait(int* addr, int expected)`. + /// We pass the base address as the first argument, So this function creates + /// an zero-initialized integer with `b` copied at the correct offset. + static int expected_value_for_futex(uint8_t b) { + int dest_val = 0; + std::memcpy(reinterpret_cast(&dest_val) + 1, &b, 1); + return dest_val; + } + + static_assert(Wait != nullptr && Wake != nullptr, ""); +}; + +//===----------------------------------------------------------------------===// +// GuardObject +//===----------------------------------------------------------------------===// + +enum class AcquireResult { + INIT_IS_DONE, + INIT_IS_PENDING, +}; +constexpr AcquireResult INIT_IS_DONE = AcquireResult::INIT_IS_DONE; +constexpr AcquireResult INIT_IS_PENDING = AcquireResult::INIT_IS_PENDING; + +/// Co-ordinates between GuardByte and InitByte. +template +struct GuardObject { + GuardObject() = delete; + GuardObject(GuardObject const&) = delete; + GuardObject& operator=(GuardObject const&) = delete; + +private: + GuardByte guard_byte; + InitByteT init_byte; + +public: + /// ARM Constructor + explicit GuardObject(uint32_t* raw_guard_object) + : guard_byte(reinterpret_cast(raw_guard_object)), + init_byte(reinterpret_cast(raw_guard_object) + 1, nullptr) {} + + /// Itanium Constructor + explicit GuardObject(uint64_t* raw_guard_object) + : guard_byte(reinterpret_cast(raw_guard_object)), + init_byte(reinterpret_cast(raw_guard_object) + 1, reinterpret_cast(raw_guard_object) + 1) { + } + + /// Implements __cxa_guard_acquire. + AcquireResult cxa_guard_acquire() { + // Use short-circuit evaluation to avoid calling init_byte.acquire when + // guard_byte.acquire returns true. (i.e. don't call it when we know from + // the guard byte that initialization has already been completed) + if (guard_byte.acquire() || init_byte.acquire()) + return INIT_IS_DONE; + return INIT_IS_PENDING; + } + + /// Implements __cxa_guard_release. + void cxa_guard_release() { + // Update guard byte first, so if somebody is woken up by init_byte.release + // and comes all the way back around to __cxa_guard_acquire again, they see + // it as having completed initialization. + guard_byte.release(); + init_byte.release(); + } + + /// Implements __cxa_guard_abort. + void cxa_guard_abort() { + guard_byte.abort(); + init_byte.abort(); + } +}; + +//===----------------------------------------------------------------------===// +// Convenience Classes +//===----------------------------------------------------------------------===// + +/// NoThreadsGuard - Manages initialization without performing any inter-thread +/// synchronization. +using NoThreadsGuard = GuardObject; + +/// GlobalMutexGuard - Manages initialization using a global mutex and +/// condition variable. +template +using GlobalMutexGuard = GuardObject>; + +/// FutexGuard - Manages initialization using atomics and the futex syscall for +/// waiting and waking. +template +using FutexGuard = GuardObject>; + +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +template +struct GlobalStatic { + static T instance; +}; +template +_LIBCPP_CONSTINIT T GlobalStatic::instance = {}; + +enum class Implementation { NoThreads, GlobalMutex, Futex }; + +template +struct SelectImplementation; + +template <> +struct SelectImplementation { + using type = NoThreadsGuard; +}; + +template <> +struct SelectImplementation { + using type = GlobalMutexGuard::instance, + GlobalStatic::instance, PlatformThreadID>; +}; + +template <> +struct SelectImplementation { + using type = FutexGuard; +}; + +// TODO(EricWF): We should prefer the futex implementation when available. But +// it should be done in a separate step from adding the implementation. +constexpr Implementation CurrentImplementation = +#if defined(_LIBCXXABI_HAS_NO_THREADS) + Implementation::NoThreads; +#elif defined(_LIBCXXABI_USE_FUTEX) + Implementation::Futex; +#else + Implementation::GlobalMutex; +#endif + +static_assert(CurrentImplementation != Implementation::Futex || PlatformSupportsFutex(), + "Futex selected but not supported"); + +using SelectedImplementation = SelectImplementation::type; + +} // end namespace +} // end namespace __cxxabiv1 + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif // LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H diff --git a/third_party/libcxxabi/cxa_handlers.cc b/third_party/libcxxabi/cxa_handlers.cc new file mode 100644 index 000000000..00d017628 --- /dev/null +++ b/third_party/libcxxabi/cxa_handlers.cc @@ -0,0 +1,101 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the functionality associated with the terminate_handler, +// unexpected_handler, and new_handler. +//===----------------------------------------------------------------------===// + +#include "third_party/libcxx/stdexcept" +#include "third_party/libcxx/new" +#include "third_party/libcxx/exception" +#include "third_party/libcxxabi/abort_message.h" +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxxabi/cxa_handlers.h" +#include "third_party/libcxxabi/cxa_exception.h" +#include "third_party/libcxxabi/private_typeinfo.h" +#include "third_party/libcxx/include/atomic_support.hh" // from libc++ + +namespace std +{ + +unexpected_handler +get_unexpected() noexcept +{ + return __libcpp_atomic_load(&__cxa_unexpected_handler, _AO_Acquire); +} + +void +__unexpected(unexpected_handler func) +{ + func(); + // unexpected handler should not return + abort_message("unexpected_handler unexpectedly returned"); +} + +__attribute__((noreturn)) +void +unexpected() +{ + __unexpected(get_unexpected()); +} + +terminate_handler +get_terminate() noexcept +{ + return __libcpp_atomic_load(&__cxa_terminate_handler, _AO_Acquire); +} + +void +__terminate(terminate_handler func) noexcept +{ +#ifndef _LIBCXXABI_NO_EXCEPTIONS + try + { +#endif // _LIBCXXABI_NO_EXCEPTIONS + func(); + // handler should not return + abort_message("terminate_handler unexpectedly returned"); +#ifndef _LIBCXXABI_NO_EXCEPTIONS + } + catch (...) + { + // handler should not throw exception + abort_message("terminate_handler unexpectedly threw an exception"); + } +#endif // _LIBCXXABI_NO_EXCEPTIONS +} + +__attribute__((noreturn)) +void +terminate() noexcept +{ +#ifndef _LIBCXXABI_NO_EXCEPTIONS + // If there might be an uncaught exception + using namespace __cxxabiv1; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals) + { + __cxa_exception* exception_header = globals->caughtExceptions; + if (exception_header) + { + _Unwind_Exception* unwind_exception = + reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; + if (__isOurExceptionClass(unwind_exception)) + __terminate(exception_header->terminateHandler); + } + } +#endif + __terminate(get_terminate()); +} + +new_handler +get_new_handler() noexcept +{ + return __libcpp_atomic_load(&__cxa_new_handler, _AO_Acquire); +} + +} // std diff --git a/third_party/libcxxabi/cxa_handlers.h b/third_party/libcxxabi/cxa_handlers.h new file mode 100644 index 000000000..04b8f771c --- /dev/null +++ b/third_party/libcxxabi/cxa_handlers.h @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the functionality associated with the terminate_handler, +// unexpected_handler, and new_handler. +//===----------------------------------------------------------------------===// + +#ifndef _CXA_HANDLERS_H +#define _CXA_HANDLERS_H + +#include "third_party/libcxxabi/include/__cxxabi_config.h" + +#include "third_party/libcxx/exception" + +namespace std +{ + +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN +void +__unexpected(unexpected_handler func); + +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN +void +__terminate(terminate_handler func) noexcept; + +} // std + +extern "C" +{ + +_LIBCXXABI_DATA_VIS extern void (*__cxa_terminate_handler)(); +_LIBCXXABI_DATA_VIS extern void (*__cxa_unexpected_handler)(); +_LIBCXXABI_DATA_VIS extern void (*__cxa_new_handler)(); + +/* + + At some point in the future these three symbols will become + C++11 atomic variables: + + extern std::atomic __cxa_terminate_handler; + extern std::atomic __cxa_unexpected_handler; + extern std::atomic __cxa_new_handler; + + This change will not impact their ABI. But it will allow for a + portable performance optimization. + +*/ + +} // extern "C" + +#endif // _CXA_HANDLERS_H diff --git a/third_party/libcxxabi/cxa_personality.cc b/third_party/libcxxabi/cxa_personality.cc new file mode 100644 index 000000000..625f1d359 --- /dev/null +++ b/third_party/libcxxabi/cxa_personality.cc @@ -0,0 +1,1315 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the "Exception Handling APIs" +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// http://www.intel.com/design/itanium/downloads/245358.htm +// +//===----------------------------------------------------------------------===// + +#include "libc/isystem/assert.h" +#include "libc/isystem/stdlib.h" +#include "libc/isystem/string.h" +#include "third_party/libcxx/typeinfo" + +#include "third_party/libcxxabi/include/__cxxabi_config.h" +#include "third_party/libcxxabi/cxa_exception.h" +#include "third_party/libcxxabi/cxa_handlers.h" +#include "third_party/libcxxabi/private_typeinfo.h" +#include "third_party/libunwind/include/unwind.h" + +// TODO: This is a temporary workaround for libc++abi to recognize that it's being +// built against LLVM's libunwind. LLVM's libunwind started reporting _LIBUNWIND_VERSION +// in LLVM 15 -- we can remove this workaround after shipping LLVM 17. Once we remove +// this workaround, it won't be possible to build libc++abi against libunwind headers +// from LLVM 14 and before anymore. +#if defined(____LIBUNWIND_CONFIG_H__) && !defined(_LIBUNWIND_VERSION) +# define _LIBUNWIND_VERSION +#endif + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +#include +#include + +extern "C" EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, + void *, PCONTEXT, + PDISPATCHER_CONTEXT, + _Unwind_Personality_Fn); +#endif + +/* + Exception Header Layout: + ++---------------------------+-----------------------------+---------------+ +| __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object | ++---------------------------+-----------------------------+---------------+ + ^ + | + +-------------------------------------------------------+ + | ++---------------------------+-----------------------------+ +| __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 | ++---------------------------+-----------------------------+ + + Exception Handling Table Layout: + ++-----------------+--------+ +| lpStartEncoding | (char) | ++---------+-------+--------+---------------+-----------------------+ +| lpStart | (encoded with lpStartEncoding) | defaults to funcStart | ++---------+-----+--------+-----------------+---------------+-------+ +| ttypeEncoding | (char) | Encoding of the type_info table | ++---------------+-+------+----+----------------------------+----------------+ +| classInfoOffset | (ULEB128) | Offset to type_info table, defaults to null | ++-----------------++--------+-+----------------------------+----------------+ +| callSiteEncoding | (char) | Encoding for Call Site Table | ++------------------+--+-----+-----+------------------------+--------------------------+ +| callSiteTableLength | (ULEB128) | Call Site Table length, used to find Action table | ++---------------------+-----------+---------------------------------------------------+ +#ifndef __USING_SJLJ_EXCEPTIONS__ ++---------------------+-----------+------------------------------------------------+ +| Beginning of Call Site Table The current ip lies within the | +| ... (start, length) range of one of these | +| call sites. There may be action needed. | +| +-------------+---------------------------------+------------------------------+ | +| | start | (encoded with callSiteEncoding) | offset relative to funcStart | | +| | length | (encoded with callSiteEncoding) | length of code fragment | | +| | landingPad | (encoded with callSiteEncoding) | offset relative to lpStart | | +| | actionEntry | (ULEB128) | Action Table Index 1-based | | +| | | | actionEntry == 0 -> cleanup | | +| +-------------+---------------------------------+------------------------------+ | +| ... | ++----------------------------------------------------------------------------------+ +#else // __USING_SJLJ_EXCEPTIONS__ ++---------------------+-----------+------------------------------------------------+ +| Beginning of Call Site Table The current ip is a 1-based index into | +| ... this table. Or it is -1 meaning no | +| action is needed. Or it is 0 meaning | +| terminate. | +| +-------------+---------------------------------+------------------------------+ | +| | landingPad | (ULEB128) | offset relative to lpStart | | +| | actionEntry | (ULEB128) | Action Table Index 1-based | | +| | | | actionEntry == 0 -> cleanup | | +| +-------------+---------------------------------+------------------------------+ | +| ... | ++----------------------------------------------------------------------------------+ +#endif // __USING_SJLJ_EXCEPTIONS__ ++---------------------------------------------------------------------+ +| Beginning of Action Table ttypeIndex == 0 : cleanup | +| ... ttypeIndex > 0 : catch | +| ttypeIndex < 0 : exception spec | +| +--------------+-----------+--------------------------------------+ | +| | ttypeIndex | (SLEB128) | Index into type_info Table (1-based) | | +| | actionOffset | (SLEB128) | Offset into next Action Table entry | | +| +--------------+-----------+--------------------------------------+ | +| ... | ++---------------------------------------------------------------------+-----------------+ +| type_info Table, but classInfoOffset does *not* point here! | +| +----------------+------------------------------------------------+-----------------+ | +| | Nth type_info* | Encoded with ttypeEncoding, 0 means catch(...) | ttypeIndex == N | | +| +----------------+------------------------------------------------+-----------------+ | +| ... | +| +----------------+------------------------------------------------+-----------------+ | +| | 1st type_info* | Encoded with ttypeEncoding, 0 means catch(...) | ttypeIndex == 1 | | +| +----------------+------------------------------------------------+-----------------+ | +| +---------------------------------------+-----------+------------------------------+ | +| | 1st ttypeIndex for 1st exception spec | (ULEB128) | classInfoOffset points here! | | +| | ... | (ULEB128) | | | +| | Mth ttypeIndex for 1st exception spec | (ULEB128) | | | +| | 0 | (ULEB128) | | | +| +---------------------------------------+------------------------------------------+ | +| ... | +| +---------------------------------------+------------------------------------------+ | +| | 0 | (ULEB128) | throw() | | +| +---------------------------------------+------------------------------------------+ | +| ... | +| +---------------------------------------+------------------------------------------+ | +| | 1st ttypeIndex for Nth exception spec | (ULEB128) | | | +| | ... | (ULEB128) | | | +| | Mth ttypeIndex for Nth exception spec | (ULEB128) | | | +| | 0 | (ULEB128) | | | +| +---------------------------------------+------------------------------------------+ | ++---------------------------------------------------------------------------------------+ + +Notes: + +* ttypeIndex in the Action Table, and in the exception spec table, is an index, + not a byte count, if positive. It is a negative index offset of + classInfoOffset and the sizeof entry depends on ttypeEncoding. + But if ttypeIndex is negative, it is a positive 1-based byte offset into the + type_info Table. + And if ttypeIndex is zero, it refers to a catch (...). + +* landingPad can be 0, this implies there is nothing to be done. + +* landingPad != 0 and actionEntry == 0 implies a cleanup needs to be done + @landingPad. + +* A cleanup can also be found under landingPad != 0 and actionEntry != 0 in + the Action Table with ttypeIndex == 0. +*/ + +namespace __cxxabiv1 +{ + +namespace +{ + +template +uintptr_t readPointerHelper(const uint8_t*& p) { + AsType value; + memcpy(&value, p, sizeof(AsType)); + p += sizeof(AsType); + return static_cast(value); +} + +} // end namespace + +extern "C" +{ + +// private API + +// Heavily borrowed from llvm/examples/ExceptionDemo/ExceptionDemo.cpp + +// DWARF Constants +enum +{ + DW_EH_PE_absptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + +/// Read a uleb128 encoded value and advance pointer +/// See Variable Length Data Appendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +uintptr_t +readULEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + return result; +} + +/// Read a sleb128 encoded value and advance pointer +/// See Variable Length Data Appendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +intptr_t +readSLEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + if ((byte & 0x40) && (shift < (sizeof(result) << 3))) + result |= static_cast(~0) << shift; + return static_cast(result); +} + +/// Read a pointer encoded value and advance pointer +/// See Variable Length Data in: +/// @link http://dwarfstd.org/Dwarf3.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @param encoding dwarf encoding type +/// @param base for adding relative offset, default to 0 +/// @returns decoded value +static +uintptr_t +readEncodedPointer(const uint8_t** data, uint8_t encoding, uintptr_t base = 0) +{ + uintptr_t result = 0; + if (encoding == DW_EH_PE_omit) + return result; + const uint8_t* p = *data; + // first get value + switch (encoding & 0x0F) + { + case DW_EH_PE_absptr: + result = readPointerHelper(p); + break; + case DW_EH_PE_uleb128: + result = readULEB128(&p); + break; + case DW_EH_PE_sleb128: + result = static_cast(readSLEB128(&p)); + break; + case DW_EH_PE_udata2: + result = readPointerHelper(p); + break; + case DW_EH_PE_udata4: + result = readPointerHelper(p); + break; + case DW_EH_PE_udata8: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata2: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata4: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata8: + result = readPointerHelper(p); + break; + default: + // not supported + abort(); + break; + } + // then add relative offset + switch (encoding & 0x70) + { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + if (result) + result += (uintptr_t)(*data); + break; + case DW_EH_PE_datarel: + assert((base != 0) && "DW_EH_PE_datarel is invalid with a base of 0"); + if (result) + result += base; + break; + case DW_EH_PE_textrel: + case DW_EH_PE_funcrel: + case DW_EH_PE_aligned: + default: + // not supported + abort(); + break; + } + // then apply indirection + if (result && (encoding & DW_EH_PE_indirect)) + result = *((uintptr_t*)result); + *data = p; + return result; +} + +static +void +call_terminate(bool native_exception, _Unwind_Exception* unwind_exception) +{ + __cxa_begin_catch(unwind_exception); + if (native_exception) + { + // Use the stored terminate_handler if possible + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + std::__terminate(exception_header->terminateHandler); + } + std::terminate(); +} + +#if defined(_LIBCXXABI_ARM_EHABI) +static const void* read_target2_value(const void* ptr) +{ + uintptr_t offset = *reinterpret_cast(ptr); + if (!offset) + return 0; + // "ARM EABI provides a TARGET2 relocation to describe these typeinfo + // pointers. The reason being it allows their precise semantics to be + // deferred to the linker. For bare-metal they turn into absolute + // relocations. For linux they turn into GOT-REL relocations." + // https://gcc.gnu.org/ml/gcc-patches/2009-08/msg00264.html +#if defined(LIBCXXABI_BAREMETAL) + return reinterpret_cast(reinterpret_cast(ptr) + + offset); +#else + return *reinterpret_cast(reinterpret_cast(ptr) + + offset); +#endif +} + +static const __shim_type_info* +get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, bool native_exception, + _Unwind_Exception* unwind_exception, uintptr_t /*base*/ = 0) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); + (void)ttypeEncoding; + + const uint8_t* ttypePtr = classInfo - ttypeIndex * sizeof(uintptr_t); + return reinterpret_cast( + read_target2_value(ttypePtr)); +} +#else // !defined(_LIBCXXABI_ARM_EHABI) +static +const __shim_type_info* +get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, bool native_exception, + _Unwind_Exception* unwind_exception, uintptr_t base = 0) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + switch (ttypeEncoding & 0x0F) + { + case DW_EH_PE_absptr: + ttypeIndex *= sizeof(void*); + break; + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + ttypeIndex *= 2; + break; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + ttypeIndex *= 4; + break; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + ttypeIndex *= 8; + break; + default: + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + classInfo -= ttypeIndex; + return (const __shim_type_info*)readEncodedPointer(&classInfo, + ttypeEncoding, base); +} +#endif // !defined(_LIBCXXABI_ARM_EHABI) + +/* + This is checking a thrown exception type, excpType, against a possibly empty + list of catchType's which make up an exception spec. + + An exception spec acts like a catch handler, but in reverse. This "catch + handler" will catch an excpType if and only if none of the catchType's in + the list will catch a excpType. If any catchType in the list can catch an + excpType, then this exception spec does not catch the excpType. +*/ +#if defined(_LIBCXXABI_ARM_EHABI) +static +bool +exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, const __shim_type_info* excpType, + void* adjustedPtr, _Unwind_Exception* unwind_exception, + uintptr_t /*base*/ = 0) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(false, unwind_exception); + } + + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); + (void)ttypeEncoding; + + // specIndex is negative of 1-based byte offset into classInfo; + specIndex = -specIndex; + --specIndex; + const void** temp = reinterpret_cast( + reinterpret_cast(classInfo) + + static_cast(specIndex) * sizeof(uintptr_t)); + // If any type in the spec list can catch excpType, return false, else return true + // adjustments to adjustedPtr are ignored. + while (true) + { + // ARM EHABI exception specification table (filter table) consists of + // several pointers which will directly point to the type info object + // (instead of ttypeIndex). The table will be terminated with 0. + const void** ttypePtr = temp++; + if (*ttypePtr == 0) + break; + // We can get the __shim_type_info simply by performing a + // R_ARM_TARGET2 relocation, and cast the result to __shim_type_info. + const __shim_type_info* catchType = + static_cast(read_target2_value(ttypePtr)); + void* tempPtr = adjustedPtr; + if (catchType->can_catch(excpType, tempPtr)) + return false; + } + return true; +} +#else +static +bool +exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, const __shim_type_info* excpType, + void* adjustedPtr, _Unwind_Exception* unwind_exception, + uintptr_t base = 0) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(false, unwind_exception); + } + // specIndex is negative of 1-based byte offset into classInfo; + specIndex = -specIndex; + --specIndex; + const uint8_t* temp = classInfo + specIndex; + // If any type in the spec list can catch excpType, return false, else return true + // adjustments to adjustedPtr are ignored. + while (true) + { + uint64_t ttypeIndex = readULEB128(&temp); + if (ttypeIndex == 0) + break; + const __shim_type_info* catchType = get_shim_type_info(ttypeIndex, + classInfo, + ttypeEncoding, + true, + unwind_exception, + base); + void* tempPtr = adjustedPtr; + if (catchType->can_catch(excpType, tempPtr)) + return false; + } + return true; +} +#endif + +static +void* +get_thrown_object_ptr(_Unwind_Exception* unwind_exception) +{ + // Even for foreign exceptions, the exception object is *probably* at unwind_exception + 1 + // Regardless, this library is prohibited from touching a foreign exception + void* adjustedPtr = unwind_exception + 1; + if (__getExceptionClass(unwind_exception) == kOurDependentExceptionClass) + adjustedPtr = ((__cxa_dependent_exception*)adjustedPtr - 1)->primaryException; + return adjustedPtr; +} + +namespace +{ + +struct scan_results +{ + int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup + const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance. + const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected + uintptr_t landingPad; // null -> nothing found, else something found + void* adjustedPtr; // Used in cxa_exception.cpp + _Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR, + // _URC_FATAL_PHASE2_ERROR, + // _URC_CONTINUE_UNWIND, + // _URC_HANDLER_FOUND +}; + +} // unnamed namespace + +static +void +set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, + const scan_results& results) +{ +#if defined(__USING_SJLJ_EXCEPTIONS__) +#define __builtin_eh_return_data_regno(regno) regno +#elif defined(__ibmxl__) +// IBM xlclang++ compiler does not support __builtin_eh_return_data_regno. +#define __builtin_eh_return_data_regno(regno) regno + 3 +#endif + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), + reinterpret_cast(unwind_exception)); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), + static_cast(results.ttypeIndex)); + _Unwind_SetIP(context, results.landingPad); +} + +/* + There are 3 types of scans needed: + + 1. Scan for handler with native or foreign exception. If handler found, + save state and return _URC_HANDLER_FOUND, else return _URC_CONTINUE_UNWIND. + May also report an error on invalid input. + May terminate for invalid exception table. + _UA_SEARCH_PHASE + + 2. Scan for handler with foreign exception. Must return _URC_HANDLER_FOUND, + or call terminate. + _UA_CLEANUP_PHASE && _UA_HANDLER_FRAME && !native_exception + + 3. Scan for cleanups. If a handler is found and this isn't forced unwind, + then terminate, otherwise ignore the handler and keep looking for cleanup. + If a cleanup is found, return _URC_HANDLER_FOUND, else return _URC_CONTINUE_UNWIND. + May also report an error on invalid input. + May terminate for invalid exception table. + _UA_CLEANUP_PHASE && !_UA_HANDLER_FRAME +*/ + +static void scan_eh_tab(scan_results &results, _Unwind_Action actions, + bool native_exception, + _Unwind_Exception *unwind_exception, + _Unwind_Context *context) { + // Initialize results to found nothing but an error + results.ttypeIndex = 0; + results.actionRecord = 0; + results.languageSpecificData = 0; + results.landingPad = 0; + results.adjustedPtr = 0; + results.reason = _URC_FATAL_PHASE1_ERROR; + // Check for consistent actions + if (actions & _UA_SEARCH_PHASE) + { + // Do Phase 1 + if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) + { + // None of these flags should be set during Phase 1 + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + } + else if (actions & _UA_CLEANUP_PHASE) + { + if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) + { + // _UA_HANDLER_FRAME should only be set if phase 1 found a handler. + // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened. + // Client error + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } + } + else // Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set + { + // One of these should be set. + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + // Start scan by getting exception table address. + const uint8_t *lsda = (const uint8_t *)_Unwind_GetLanguageSpecificData(context); + if (lsda == 0) + { + // There is no exception table + results.reason = _URC_CONTINUE_UNWIND; + return; + } + results.languageSpecificData = lsda; +#if defined(_AIX) + uintptr_t base = _Unwind_GetDataRelBase(context); +#else + uintptr_t base = 0; +#endif + // Get the current instruction pointer and offset it before next + // instruction in the current frame which threw the exception. + uintptr_t ip = _Unwind_GetIP(context) - 1; + // Get beginning current frame's code (as defined by the + // emitted dwarf code) + uintptr_t funcStart = _Unwind_GetRegionStart(context); +#ifdef __USING_SJLJ_EXCEPTIONS__ + if (ip == uintptr_t(-1)) + { + // no action + results.reason = _URC_CONTINUE_UNWIND; + return; + } + else if (ip == 0) + call_terminate(native_exception, unwind_exception); + // ip is 1-based index into call site table +#else // !__USING_SJLJ_EXCEPTIONS__ + uintptr_t ipOffset = ip - funcStart; +#endif // !defined(_USING_SLJL_EXCEPTIONS__) + const uint8_t* classInfo = NULL; + // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding + // dwarf emission + // Parse LSDA header. + uint8_t lpStartEncoding = *lsda++; + const uint8_t* lpStart = + (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding, base); + if (lpStart == 0) + lpStart = (const uint8_t*)funcStart; + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding != DW_EH_PE_omit) + { + // Calculate type info locations in emitted dwarf code which + // were flagged by type info arguments to llvm.eh.selector + // intrinsic + uintptr_t classInfoOffset = readULEB128(&lsda); + classInfo = lsda + classInfoOffset; + } + // Walk call-site table looking for range that + // includes current PC. + uint8_t callSiteEncoding = *lsda++; +#ifdef __USING_SJLJ_EXCEPTIONS__ + (void)callSiteEncoding; // When using SjLj exceptions, callSiteEncoding is never used +#endif + uint32_t callSiteTableLength = static_cast(readULEB128(&lsda)); + const uint8_t* callSiteTableStart = lsda; + const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; + const uint8_t* actionTableStart = callSiteTableEnd; + const uint8_t* callSitePtr = callSiteTableStart; + while (callSitePtr < callSiteTableEnd) + { + // There is one entry per call site. +#ifndef __USING_SJLJ_EXCEPTIONS__ + // The call sites are non-overlapping in [start, start+length) + // The call sites are ordered in increasing value of start + uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t actionEntry = readULEB128(&callSitePtr); + if ((start <= ipOffset) && (ipOffset < (start + length))) +#else // __USING_SJLJ_EXCEPTIONS__ + // ip is 1-based index into this table + uintptr_t landingPad = readULEB128(&callSitePtr); + uintptr_t actionEntry = readULEB128(&callSitePtr); + if (--ip == 0) +#endif // __USING_SJLJ_EXCEPTIONS__ + { + // Found the call site containing ip. +#ifndef __USING_SJLJ_EXCEPTIONS__ + if (landingPad == 0) + { + // No handler here + results.reason = _URC_CONTINUE_UNWIND; + return; + } + landingPad = (uintptr_t)lpStart + landingPad; +#else // __USING_SJLJ_EXCEPTIONS__ + ++landingPad; +#endif // __USING_SJLJ_EXCEPTIONS__ + results.landingPad = landingPad; + if (actionEntry == 0) + { + // Found a cleanup + results.reason = actions & _UA_SEARCH_PHASE + ? _URC_CONTINUE_UNWIND + : _URC_HANDLER_FOUND; + return; + } + // Convert 1-based byte offset into + const uint8_t* action = actionTableStart + (actionEntry - 1); + bool hasCleanup = false; + // Scan action entries until you find a matching handler, cleanup, or the end of action list + while (true) + { + const uint8_t* actionRecord = action; + int64_t ttypeIndex = readSLEB128(&action); + if (ttypeIndex > 0) + { + // Found a catch, does it actually catch? + // First check for catch (...) + const __shim_type_info* catchType = + get_shim_type_info(static_cast(ttypeIndex), + classInfo, ttypeEncoding, + native_exception, unwind_exception, + base); + if (catchType == 0) + { + // Found catch (...) catches everything, including + // foreign exceptions. This is search phase, cleanup + // phase with foreign exception, or forced unwinding. + assert(actions & (_UA_SEARCH_PHASE | _UA_HANDLER_FRAME | + _UA_FORCE_UNWIND)); + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.adjustedPtr = + get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + // Else this is a catch (T) clause and will never + // catch a foreign exception + else if (native_exception) + { + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + void* adjustedPtr = get_thrown_object_ptr(unwind_exception); + const __shim_type_info* excpType = + static_cast(exception_header->exceptionType); + if (adjustedPtr == 0 || excpType == 0) + { + // Something very bad happened + call_terminate(native_exception, unwind_exception); + } + if (catchType->can_catch(excpType, adjustedPtr)) + { + // Found a matching handler. This is either search + // phase or forced unwinding. + assert(actions & + (_UA_SEARCH_PHASE | _UA_FORCE_UNWIND)); + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.adjustedPtr = adjustedPtr; + results.reason = _URC_HANDLER_FOUND; + return; + } + } + // Scan next action ... + } + else if (ttypeIndex < 0) + { + // Found an exception specification. + if (actions & _UA_FORCE_UNWIND) { + // Skip if forced unwinding. + } else if (native_exception) { + // Does the exception spec catch this native exception? + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + void* adjustedPtr = get_thrown_object_ptr(unwind_exception); + const __shim_type_info* excpType = + static_cast(exception_header->exceptionType); + if (adjustedPtr == 0 || excpType == 0) + { + // Something very bad happened + call_terminate(native_exception, unwind_exception); + } + if (exception_spec_can_catch(ttypeIndex, classInfo, + ttypeEncoding, excpType, + adjustedPtr, + unwind_exception, base)) + { + // Native exception caught by exception + // specification. + assert(actions & _UA_SEARCH_PHASE); + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.adjustedPtr = adjustedPtr; + results.reason = _URC_HANDLER_FOUND; + return; + } + } else { + // foreign exception caught by exception spec + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.adjustedPtr = + get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + // Scan next action ... + } else { + hasCleanup = true; + } + const uint8_t* temp = action; + int64_t actionOffset = readSLEB128(&temp); + if (actionOffset == 0) + { + // End of action list. If this is phase 2 and we have found + // a cleanup (ttypeIndex=0), return _URC_HANDLER_FOUND; + // otherwise return _URC_CONTINUE_UNWIND. + results.reason = hasCleanup && actions & _UA_CLEANUP_PHASE + ? _URC_HANDLER_FOUND + : _URC_CONTINUE_UNWIND; + return; + } + // Go to next action + action += actionOffset; + } // there is no break out of this loop, only return + } +#ifndef __USING_SJLJ_EXCEPTIONS__ + else if (ipOffset < start) + { + // There is no call site for this ip + // Something bad has happened. We should never get here. + // Possible stack corruption. + call_terminate(native_exception, unwind_exception); + } +#endif // !__USING_SJLJ_EXCEPTIONS__ + } // there might be some tricky cases which break out of this loop + + // It is possible that no eh table entry specify how to handle + // this exception. By spec, terminate it immediately. + call_terminate(native_exception, unwind_exception); +} + +// public API + +/* +The personality function branches on actions like so: + +_UA_SEARCH_PHASE + + If _UA_CLEANUP_PHASE or _UA_HANDLER_FRAME or _UA_FORCE_UNWIND there's + an error from above, return _URC_FATAL_PHASE1_ERROR. + + Scan for anything that could stop unwinding: + + 1. A catch clause that will catch this exception + (will never catch foreign). + 2. A catch (...) (will always catch foreign). + 3. An exception spec that will catch this exception + (will always catch foreign). + If a handler is found + If not foreign + Save state in header + return _URC_HANDLER_FOUND + Else a handler not found + return _URC_CONTINUE_UNWIND + +_UA_CLEANUP_PHASE + + If _UA_HANDLER_FRAME + If _UA_FORCE_UNWIND + How did this happen? return _URC_FATAL_PHASE2_ERROR + If foreign + Do _UA_SEARCH_PHASE to recover state + else + Recover state from header + Transfer control to landing pad. return _URC_INSTALL_CONTEXT + + Else + + This branch handles both normal C++ non-catching handlers (cleanups) + and forced unwinding. + Scan for anything that can not stop unwinding: + + 1. A cleanup. + + If a cleanup is found + transfer control to it. return _URC_INSTALL_CONTEXT + Else a cleanup is not found: return _URC_CONTINUE_UNWIND +*/ + +#if !defined(_LIBCXXABI_ARM_EHABI) +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +static _Unwind_Reason_Code __gxx_personality_imp +#else +_LIBCXXABI_FUNC_VIS _Unwind_Reason_Code +#ifdef __USING_SJLJ_EXCEPTIONS__ +__gxx_personality_sj0 +#elif defined(__MVS__) +__zos_cxx_personality_v2 +#else +__gxx_personality_v0 +#endif +#endif + (int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) +{ + if (version != 1 || unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + + bool native_exception = (exceptionClass & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + scan_results results; + // Process a catch handler for a native exception first. + if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME) && + native_exception) { + // Reload the results from the phase 1 cache. + __cxa_exception* exception_header = + (__cxa_exception*)(unwind_exception + 1) - 1; + results.ttypeIndex = exception_header->handlerSwitchValue; + results.actionRecord = exception_header->actionRecord; + results.languageSpecificData = exception_header->languageSpecificData; + results.landingPad = + reinterpret_cast(exception_header->catchTemp); + results.adjustedPtr = exception_header->adjustedPtr; + + // Jump to the handler. + set_registers(unwind_exception, context, results); + // Cache base for calculating the address of ttype in + // __cxa_call_unexpected. + if (results.ttypeIndex < 0) { +#if defined(_AIX) + exception_header->catchTemp = (void *)_Unwind_GetDataRelBase(context); +#else + exception_header->catchTemp = 0; +#endif + } + return _URC_INSTALL_CONTEXT; + } + + // In other cases we need to scan LSDA. + scan_eh_tab(results, actions, native_exception, unwind_exception, context); + if (results.reason == _URC_CONTINUE_UNWIND || + results.reason == _URC_FATAL_PHASE1_ERROR) + return results.reason; + + if (actions & _UA_SEARCH_PHASE) + { + // Phase 1 search: All we're looking for in phase 1 is a handler that + // halts unwinding + assert(results.reason == _URC_HANDLER_FOUND); + if (native_exception) { + // For a native exception, cache the LSDA result. + __cxa_exception* exc = (__cxa_exception*)(unwind_exception + 1) - 1; + exc->handlerSwitchValue = static_cast(results.ttypeIndex); + exc->actionRecord = results.actionRecord; + exc->languageSpecificData = results.languageSpecificData; + exc->catchTemp = reinterpret_cast(results.landingPad); + exc->adjustedPtr = results.adjustedPtr; + } + return _URC_HANDLER_FOUND; + } + + assert(actions & _UA_CLEANUP_PHASE); + assert(results.reason == _URC_HANDLER_FOUND); + set_registers(unwind_exception, context, results); + // Cache base for calculating the address of ttype in __cxa_call_unexpected. + if (results.ttypeIndex < 0) { + __cxa_exception* exception_header = + (__cxa_exception*)(unwind_exception + 1) - 1; +#if defined(_AIX) + exception_header->catchTemp = (void *)_Unwind_GetDataRelBase(context); +#else + exception_header->catchTemp = 0; +#endif + } + return _URC_INSTALL_CONTEXT; +} + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +extern "C" _LIBCXXABI_FUNC_VIS EXCEPTION_DISPOSITION +__gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp, + __gxx_personality_imp); +} +#endif + +#else + +extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*, + _Unwind_Context*); + +// Helper function to unwind one frame. +// ARM EHABI 7.3 and 7.4: If the personality function returns _URC_CONTINUE_UNWIND, the +// personality routine should update the virtual register set (VRS) according to the +// corresponding frame unwinding instructions (ARM EHABI 9.3.) +static _Unwind_Reason_Code continue_unwind(_Unwind_Exception* unwind_exception, + _Unwind_Context* context) +{ + switch (__gnu_unwind_frame(unwind_exception, context)) { + case _URC_OK: + return _URC_CONTINUE_UNWIND; + case _URC_END_OF_STACK: + return _URC_END_OF_STACK; + default: + return _URC_FAILURE; + } +} + +// ARM register names +#if !defined(_LIBUNWIND_VERSION) +static const uint32_t REG_UCB = 12; // Register to save _Unwind_Control_Block +#endif +static const uint32_t REG_SP = 13; + +static void save_results_to_barrier_cache(_Unwind_Exception* unwind_exception, + const scan_results& results) +{ + unwind_exception->barrier_cache.bitpattern[0] = (uint32_t)results.adjustedPtr; + unwind_exception->barrier_cache.bitpattern[1] = (uint32_t)results.actionRecord; + unwind_exception->barrier_cache.bitpattern[2] = (uint32_t)results.languageSpecificData; + unwind_exception->barrier_cache.bitpattern[3] = (uint32_t)results.landingPad; + unwind_exception->barrier_cache.bitpattern[4] = (uint32_t)results.ttypeIndex; +} + +static void load_results_from_barrier_cache(scan_results& results, + const _Unwind_Exception* unwind_exception) +{ + results.adjustedPtr = (void*)unwind_exception->barrier_cache.bitpattern[0]; + results.actionRecord = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[1]; + results.languageSpecificData = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2]; + results.landingPad = (uintptr_t)unwind_exception->barrier_cache.bitpattern[3]; + results.ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4]; +} + +extern "C" _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code +__gxx_personality_v0(_Unwind_State state, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context) +{ + if (unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + + bool native_exception = __isOurExceptionClass(unwind_exception); + +#if !defined(_LIBUNWIND_VERSION) + // Copy the address of _Unwind_Control_Block to r12 so that + // _Unwind_GetLanguageSpecificData() and _Unwind_GetRegionStart() can + // return correct address. + _Unwind_SetGR(context, REG_UCB, reinterpret_cast(unwind_exception)); +#endif + + // Check the undocumented force unwinding behavior + bool is_force_unwinding = state & _US_FORCE_UNWIND; + state &= ~_US_FORCE_UNWIND; + + scan_results results; + switch (state) { + case _US_VIRTUAL_UNWIND_FRAME: + if (is_force_unwinding) + return continue_unwind(unwind_exception, context); + + // Phase 1 search: All we're looking for in phase 1 is a handler that halts unwinding + scan_eh_tab(results, _UA_SEARCH_PHASE, native_exception, unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + unwind_exception->barrier_cache.sp = _Unwind_GetGR(context, REG_SP); + if (native_exception) + save_results_to_barrier_cache(unwind_exception, results); + return _URC_HANDLER_FOUND; + } + // Did not find the catch handler + if (results.reason == _URC_CONTINUE_UNWIND) + return continue_unwind(unwind_exception, context); + return results.reason; + + case _US_UNWIND_FRAME_STARTING: + // TODO: Support force unwinding in the phase 2 search. + // NOTE: In order to call the cleanup functions, _Unwind_ForcedUnwind() + // will call this personality function with (_US_FORCE_UNWIND | + // _US_UNWIND_FRAME_STARTING). + + // Phase 2 search + if (unwind_exception->barrier_cache.sp == _Unwind_GetGR(context, REG_SP)) + { + // Found a catching handler in phase 1 + if (native_exception) + { + // Load the result from the native exception barrier cache. + load_results_from_barrier_cache(results, unwind_exception); + results.reason = _URC_HANDLER_FOUND; + } + else + { + // Search for the catching handler again for the foreign exception. + scan_eh_tab(results, static_cast<_Unwind_Action>(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME), + native_exception, unwind_exception, context); + if (results.reason != _URC_HANDLER_FOUND) // phase1 search should guarantee to find one + call_terminate(native_exception, unwind_exception); + } + + // Install the context for the catching handler + set_registers(unwind_exception, context, results); + return _URC_INSTALL_CONTEXT; + } + + // Either we didn't do a phase 1 search (due to forced unwinding), or + // phase 1 reported no catching-handlers. + // Search for a (non-catching) cleanup + if (is_force_unwinding) + scan_eh_tab( + results, + static_cast<_Unwind_Action>(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND), + native_exception, unwind_exception, context); + else + scan_eh_tab(results, _UA_CLEANUP_PHASE, native_exception, + unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + // Found a non-catching handler + + // ARM EHABI 8.4.2: Before we can jump to the cleanup handler, we have to setup some + // internal data structures, so that __cxa_end_cleanup() can get unwind_exception from + // __cxa_get_globals(). + __cxa_begin_cleanup(unwind_exception); + + // Install the context for the cleanup handler + set_registers(unwind_exception, context, results); + return _URC_INSTALL_CONTEXT; + } + + // Did not find any handler + if (results.reason == _URC_CONTINUE_UNWIND) + return continue_unwind(unwind_exception, context); + return results.reason; + + case _US_UNWIND_FRAME_RESUME: + return continue_unwind(unwind_exception, context); + } + + // We were called improperly: neither a phase 1 or phase 2 search + return _URC_FATAL_PHASE1_ERROR; +} +#endif + + +__attribute__((noreturn)) +_LIBCXXABI_FUNC_VIS void +__cxa_call_unexpected(void* arg) +{ + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg); + if (unwind_exception == 0) + call_terminate(false, unwind_exception); + __cxa_begin_catch(unwind_exception); + bool native_old_exception = __isOurExceptionClass(unwind_exception); + std::unexpected_handler u_handler; + std::terminate_handler t_handler; + __cxa_exception* old_exception_header = 0; + int64_t ttypeIndex; + const uint8_t* lsda; + uintptr_t base = 0; + + if (native_old_exception) + { + old_exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + t_handler = old_exception_header->terminateHandler; + u_handler = old_exception_header->unexpectedHandler; + // If std::__unexpected(u_handler) rethrows the same exception, + // these values get overwritten by the rethrow. So save them now: +#if defined(_LIBCXXABI_ARM_EHABI) + ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4]; + lsda = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2]; +#else + ttypeIndex = old_exception_header->handlerSwitchValue; + lsda = old_exception_header->languageSpecificData; + base = (uintptr_t)old_exception_header->catchTemp; +#endif + } + else + { + t_handler = std::get_terminate(); + u_handler = std::get_unexpected(); + } + try + { + std::__unexpected(u_handler); + } + catch (...) + { + // If the old exception is foreign, then all we can do is terminate. + // We have no way to recover the needed old exception spec. There's + // no way to pass that information here. And the personality routine + // can't call us directly and do anything but terminate() if we throw + // from here. + if (native_old_exception) + { + // Have: + // old_exception_header->languageSpecificData + // old_exception_header->actionRecord + // old_exception_header->catchTemp, base for calculating ttype + // Need + // const uint8_t* classInfo + // uint8_t ttypeEncoding + uint8_t lpStartEncoding = *lsda++; + const uint8_t* lpStart = + (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding, base); + (void)lpStart; // purposefully unused. Just needed to increment lsda. + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding == DW_EH_PE_omit) + std::__terminate(t_handler); + uintptr_t classInfoOffset = readULEB128(&lsda); + const uint8_t* classInfo = lsda + classInfoOffset; + // Is this new exception catchable by the exception spec at ttypeIndex? + // The answer is obviously yes if the new and old exceptions are the same exception + // If no + // throw; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + __cxa_exception* new_exception_header = globals->caughtExceptions; + if (new_exception_header == 0) + // This shouldn't be able to happen! + std::__terminate(t_handler); + bool native_new_exception = __isOurExceptionClass(&new_exception_header->unwindHeader); + void* adjustedPtr; + if (native_new_exception && (new_exception_header != old_exception_header)) + { + const __shim_type_info* excpType = + static_cast(new_exception_header->exceptionType); + adjustedPtr = + __getExceptionClass(&new_exception_header->unwindHeader) == kOurDependentExceptionClass ? + ((__cxa_dependent_exception*)new_exception_header)->primaryException : + new_exception_header + 1; + if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, + excpType, adjustedPtr, + unwind_exception, base)) + { + // We need to __cxa_end_catch, but for the old exception, + // not the new one. This is a little tricky ... + // Disguise new_exception_header as a rethrown exception, but + // don't actually rethrow it. This means you can temporarily + // end the catch clause enclosing new_exception_header without + // __cxa_end_catch destroying new_exception_header. + new_exception_header->handlerCount = -new_exception_header->handlerCount; + globals->uncaughtExceptions += 1; + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Call __cxa_end_catch for old_exception_header + __cxa_end_catch(); + // Renter this catch clause with new_exception_header + __cxa_begin_catch(&new_exception_header->unwindHeader); + // Rethrow new_exception_header + throw; + } + } + // Will a std::bad_exception be catchable by the exception spec at + // ttypeIndex? + // If no + // throw std::bad_exception(); + const __shim_type_info* excpType = + static_cast(&typeid(std::bad_exception)); + std::bad_exception be; + adjustedPtr = &be; + if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, + excpType, adjustedPtr, + unwind_exception, base)) + { + // We need to __cxa_end_catch for both the old exception and the + // new exception. Technically we should do it in that order. + // But it is expedient to do it in the opposite order: + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Throw std::bad_exception will __cxa_end_catch for + // old_exception_header + throw be; + } + } + } + std::__terminate(t_handler); +} + +#if defined(_AIX) +// Personality routine for EH using the range table. Make it an alias of +// __gxx_personality_v0(). +_LIBCXXABI_FUNC_VIS _Unwind_Reason_Code __xlcxx_personality_v1( + int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) + __attribute__((__alias__("__gxx_personality_v0"))); +#endif + +} // extern "C" + +} // __cxxabiv1 + +#if defined(_AIX) +// Include implementation of the personality and helper functions for the +// state table based EH used by IBM legacy compilers xlC and xlclang++ on AIX. +# include "aix_state_tab_eh.inc" +#endif diff --git a/third_party/libcxxabi/cxa_thread_atexit.cc b/third_party/libcxxabi/cxa_thread_atexit.cc new file mode 100644 index 000000000..a8f5b0b02 --- /dev/null +++ b/third_party/libcxxabi/cxa_thread_atexit.cc @@ -0,0 +1,145 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/abort_message.h" +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxx/__threading_support" +#ifndef _LIBCXXABI_HAS_NO_THREADS +#if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB) +#pragma comment(lib, "pthread") +#endif +#endif + +#include "libc/isystem/stdlib.h" + +namespace __cxxabiv1 { + + using Dtor = void(*)(void*); + + extern "C" +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + // A weak symbol is used to detect this function's presence in the C library + // at runtime, even if libc++ is built against an older libc + _LIBCXXABI_WEAK +#endif + int __cxa_thread_atexit_impl(Dtor, void*, void*); + +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + +namespace { + // This implementation is used if the C library does not provide + // __cxa_thread_atexit_impl() for us. It has a number of limitations that are + // difficult to impossible to address without ..._impl(): + // + // - dso_symbol is ignored. This means that a shared library may be unloaded + // (via dlclose()) before its thread_local destructors have run. + // + // - thread_local destructors for the main thread are run by the destructor of + // a static object. This is later than expected; they should run before the + // destructors of any objects with static storage duration. + // + // - thread_local destructors on non-main threads run on the first iteration + // through the __libccpp_tls_key destructors. + // std::notify_all_at_thread_exit() and similar functions must be careful to + // wait until the second iteration to provide their intended ordering + // guarantees. + // + // Another limitation, though one shared with ..._impl(), is that any + // thread_locals that are first initialized after non-thread_local global + // destructors begin to run will not be destroyed. [basic.start.term] states + // that all thread_local destructors are sequenced before the destruction of + // objects with static storage duration, resulting in a contradiction if a + // thread_local is constructed after that point. Thus we consider such + // programs ill-formed, and don't bother to run those destructors. (If the + // program terminates abnormally after such a thread_local is constructed, + // the destructor is not expected to run and thus there is no contradiction. + // So construction still has to work.) + + struct DtorList { + Dtor dtor; + void* obj; + DtorList* next; + }; + + // The linked list of thread-local destructors to run + __thread DtorList* dtors = nullptr; + // True if the destructors are currently scheduled to run on this thread + __thread bool dtors_alive = false; + // Used to trigger destructors on thread exit; value is ignored + std::__libcpp_tls_key dtors_key; + + void run_dtors(void*) { + while (auto head = dtors) { + dtors = head->next; + head->dtor(head->obj); + ::free(head); + } + + dtors_alive = false; + } + + struct DtorsManager { + DtorsManager() { + // There is intentionally no matching std::__libcpp_tls_delete call, as + // __cxa_thread_atexit() may be called arbitrarily late (for example, from + // global destructors or atexit() handlers). + if (std::__libcpp_tls_create(&dtors_key, run_dtors) != 0) { + abort_message("std::__libcpp_tls_create() failed in __cxa_thread_atexit()"); + } + } + + ~DtorsManager() { + // std::__libcpp_tls_key destructors do not run on threads that call exit() + // (including when the main thread returns from main()), so we explicitly + // call the destructor here. This runs at exit time (potentially earlier + // if libc++abi is dlclose()'d). Any thread_locals initialized after this + // point will not be destroyed. + run_dtors(nullptr); + } + }; +} // namespace + +#endif // HAVE___CXA_THREAD_ATEXIT_IMPL + +extern "C" { + + _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() { +#ifdef HAVE___CXA_THREAD_ATEXIT_IMPL + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); +#else + if (__cxa_thread_atexit_impl) { + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); + } else { + // Initialize the dtors std::__libcpp_tls_key (uses __cxa_guard_*() for + // one-time initialization and __cxa_atexit() for destruction) + static DtorsManager manager; + + if (!dtors_alive) { + if (std::__libcpp_tls_set(dtors_key, &dtors_key) != 0) { + return -1; + } + dtors_alive = true; + } + + auto head = static_cast(::malloc(sizeof(DtorList))); + if (!head) { + return -1; + } + + head->dtor = dtor; + head->obj = obj; + head->next = dtors; + dtors = head; + + return 0; + } +#endif // HAVE___CXA_THREAD_ATEXIT_IMPL + } + +} // extern "C" +} // namespace __cxxabiv1 diff --git a/third_party/libcxxabi/cxa_vector.cc b/third_party/libcxxabi/cxa_vector.cc new file mode 100644 index 000000000..3b5ffd781 --- /dev/null +++ b/third_party/libcxxabi/cxa_vector.cc @@ -0,0 +1,421 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the "Array Construction and Destruction APIs" +// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-ctor +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxxabi/include/__cxxabi_config.h" + +#include "third_party/libcxx/exception" // for std::terminate +#include "third_party/libcxx/new" // for std::bad_array_new_length + +#include "third_party/libcxxabi/abort_message.h" + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +namespace __cxxabiv1 { + +// +// Helper routines and classes +// + +namespace { + inline static size_t __get_element_count ( void *p ) { + return static_cast (p)[-1]; + } + + inline static void __set_element_count ( void *p, size_t element_count ) { + static_cast (p)[-1] = element_count; + } + + +// A pair of classes to simplify exception handling and control flow. +// They get passed a block of memory in the constructor, and unless the +// 'release' method is called, they deallocate the memory in the destructor. +// Preferred usage is to allocate some memory, attach it to one of these objects, +// and then, when all the operations to set up the memory block have succeeded, +// call 'release'. If any of the setup operations fail, or an exception is +// thrown, then the block is automatically deallocated. +// +// The only difference between these two classes is the signature for the +// deallocation function (to match new2/new3 and delete2/delete3. + class st_heap_block2 { + public: + typedef void (*dealloc_f)(void *); + + st_heap_block2 ( dealloc_f dealloc, void *ptr ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), enabled_ ( true ) {} + ~st_heap_block2 () { if ( enabled_ ) dealloc_ ( ptr_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + bool enabled_; + }; + + class st_heap_block3 { + public: + typedef void (*dealloc_f)(void *, size_t); + + st_heap_block3 ( dealloc_f dealloc, void *ptr, size_t size ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), size_ ( size ), enabled_ ( true ) {} + ~st_heap_block3 () { if ( enabled_ ) dealloc_ ( ptr_, size_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + size_t size_; + bool enabled_; + }; + + class st_cxa_cleanup { + public: + typedef void (*destruct_f)(void *); + + st_cxa_cleanup ( void *ptr, size_t &idx, size_t element_size, destruct_f destructor ) + : ptr_ ( ptr ), idx_ ( idx ), element_size_ ( element_size ), + destructor_ ( destructor ), enabled_ ( true ) {} + ~st_cxa_cleanup () { + if ( enabled_ ) + __cxa_vec_cleanup ( ptr_, idx_, element_size_, destructor_ ); + } + + void release () { enabled_ = false; } + + private: + void *ptr_; + size_t &idx_; + size_t element_size_; + destruct_f destructor_; + bool enabled_; + }; + + class st_terminate { + public: + st_terminate ( bool enabled = true ) : enabled_ ( enabled ) {} + ~st_terminate () { if ( enabled_ ) std::terminate (); } + void release () { enabled_ = false; } + private: + bool enabled_ ; + }; +} + +// +// Externally visible routines +// + +namespace { +_LIBCXXABI_NORETURN +void throw_bad_array_new_length() { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_array_new_length(); +#else + abort_message("__cxa_vec_new failed to allocate memory"); +#endif +} + +bool mul_overflow(size_t x, size_t y, size_t *res) { +#if (defined(_LIBCXXABI_COMPILER_CLANG) && __has_builtin(__builtin_mul_overflow)) \ + || defined(_LIBCXXABI_COMPILER_GCC) + return __builtin_mul_overflow(x, y, res); +#else + *res = x * y; + return x && ((*res / x) != y); +#endif +} + +bool add_overflow(size_t x, size_t y, size_t *res) { +#if (defined(_LIBCXXABI_COMPILER_CLANG) && __has_builtin(__builtin_add_overflow)) \ + || defined(_LIBCXXABI_COMPILER_GCC) + return __builtin_add_overflow(x, y, res); +#else + *res = x + y; + return *res < y; +#endif +} + +size_t calculate_allocation_size_or_throw(size_t element_count, + size_t element_size, + size_t padding_size) { + size_t element_heap_size; + if (mul_overflow(element_count, element_size, &element_heap_size)) + throw_bad_array_new_length(); + + size_t allocation_size; + if (add_overflow(element_heap_size, padding_size, &allocation_size)) + throw_bad_array_new_length(); + + return allocation_size; +} + +} // namespace + +extern "C" { + +// Equivalent to +// +// __cxa_vec_new2(element_count, element_size, padding_size, constructor, +// destructor, &::operator new[], &::operator delete[]) +_LIBCXXABI_FUNC_VIS void * +__cxa_vec_new(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *)) { + return __cxa_vec_new2 ( element_count, element_size, padding_size, + constructor, destructor, &::operator new [], &::operator delete [] ); +} + + +// Given the number and size of elements for an array and the non-negative +// size of prefix padding for a cookie, allocate space (using alloc) for +// the array preceded by the specified padding, initialize the cookie if +// the padding is non-zero, and call the given constructor on each element. +// Return the address of the array proper, after the padding. +// +// If alloc throws an exception, rethrow the exception. If alloc returns +// NULL, return NULL. If the constructor throws an exception, call +// destructor for any already constructed elements, and rethrow the +// exception. If the destructor throws an exception, call std::terminate. +// +// The constructor may be NULL, in which case it must not be called. If the +// padding_size is zero, the destructor may be NULL; in that case it must +// not be called. +// +// Neither alloc nor dealloc may be NULL. +_LIBCXXABI_FUNC_VIS void * +__cxa_vec_new2(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *), + void *(*alloc)(size_t), void (*dealloc)(void *)) { + const size_t heap_size = calculate_allocation_size_or_throw( + element_count, element_size, padding_size); + char* const heap_block = static_cast(alloc(heap_size)); + char* vec_base = heap_block; + + if (NULL != vec_base) { + st_heap_block2 heap(dealloc, heap_block); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Same as __cxa_vec_new2 except that the deallocation function takes both +// the object address and its size. +_LIBCXXABI_FUNC_VIS void * +__cxa_vec_new3(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *), + void *(*alloc)(size_t), void (*dealloc)(void *, size_t)) { + const size_t heap_size = calculate_allocation_size_or_throw( + element_count, element_size, padding_size); + char* const heap_block = static_cast(alloc(heap_size)); + char* vec_base = heap_block; + + if (NULL != vec_base) { + st_heap_block3 heap(dealloc, heap_block, heap_size); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Given the (data) addresses of a destination and a source array, an +// element count and an element size, call the given copy constructor to +// copy each element from the source array to the destination array. The +// copy constructor's arguments are the destination address and source +// address, respectively. If an exception occurs, call the given destructor +// (if non-NULL) on each copied element and rethrow. If the destructor +// throws an exception, call terminate(). The constructor and or destructor +// pointers may be NULL. If either is NULL, no action is taken when it +// would have been called. + +_LIBCXXABI_FUNC_VIS void __cxa_vec_cctor(void *dest_array, void *src_array, + size_t element_count, + size_t element_size, + void (*constructor)(void *, void *), + void (*destructor)(void *)) { + if ( NULL != constructor ) { + size_t idx = 0; + char *src_ptr = static_cast(src_array); + char *dest_ptr = static_cast(dest_array); + st_cxa_cleanup cleanup ( dest_array, idx, element_size, destructor ); + + for ( idx = 0; idx < element_count; + ++idx, src_ptr += element_size, dest_ptr += element_size ) + constructor ( dest_ptr, src_ptr ); + cleanup.release (); // We're good! + } +} + + +// Given the (data) address of an array, not including any cookie padding, +// and the number and size of its elements, call the given constructor on +// each element. If the constructor throws an exception, call the given +// destructor for any already-constructed elements, and rethrow the +// exception. If the destructor throws an exception, call terminate(). The +// constructor and/or destructor pointers may be NULL. If either is NULL, +// no action is taken when it would have been called. +_LIBCXXABI_FUNC_VIS void +__cxa_vec_ctor(void *array_address, size_t element_count, size_t element_size, + void (*constructor)(void *), void (*destructor)(void *)) { + if ( NULL != constructor ) { + size_t idx; + char *ptr = static_cast ( array_address ); + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + + // Construct the elements + for ( idx = 0; idx < element_count; ++idx, ptr += element_size ) + constructor ( ptr ); + cleanup.release (); // We're good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, rethrow after destroying the remaining +// elements if possible. If the destructor throws a second exception, call +// terminate(). The destructor pointer may be NULL, in which case this +// routine does nothing. +_LIBCXXABI_FUNC_VIS void __cxa_vec_dtor(void *array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void *)) { + if ( NULL != destructor ) { + char *ptr = static_cast (array_address); + size_t idx = element_count; + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + { + st_terminate exception_guard (__cxa_uncaught_exception ()); + ptr += element_count * element_size; // one past the last element + + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're good ! + } + cleanup.release (); // We're still good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, call terminate(). The destructor pointer +// may be NULL, in which case this routine does nothing. +_LIBCXXABI_FUNC_VIS void __cxa_vec_cleanup(void *array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void *)) { + if ( NULL != destructor ) { + char *ptr = static_cast (array_address); + size_t idx = element_count; + st_terminate exception_guard; + + ptr += element_count * element_size; // one past the last element + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're done! + } +} + + +// If the array_address is NULL, return immediately. Otherwise, given the +// (data) address of an array, the non-negative size of prefix padding for +// the cookie, and the size of its elements, call the given destructor on +// each element, using the cookie to determine the number of elements, and +// then delete the space by calling ::operator delete[](void *). If the +// destructor throws an exception, rethrow after (a) destroying the +// remaining elements, and (b) deallocating the storage. If the destructor +// throws a second exception, call terminate(). If padding_size is 0, the +// destructor pointer must be NULL. If the destructor pointer is NULL, no +// destructor call is to be made. +// +// The intent of this function is to permit an implementation to call this +// function when confronted with an expression of the form delete[] p in +// the source code, provided that the default deallocation function can be +// used. Therefore, the semantics of this function are consistent with +// those required by the standard. The requirement that the deallocation +// function be called even if the destructor throws an exception derives +// from the resolution to DR 353 to the C++ standard, which was adopted in +// April, 2003. +_LIBCXXABI_FUNC_VIS void __cxa_vec_delete(void *array_address, + size_t element_size, + size_t padding_size, + void (*destructor)(void *)) { + __cxa_vec_delete2 ( array_address, element_size, padding_size, + destructor, &::operator delete [] ); +} + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +_LIBCXXABI_FUNC_VIS void +__cxa_vec_delete2(void *array_address, size_t element_size, size_t padding_size, + void (*destructor)(void *), void (*dealloc)(void *)) { + if ( NULL != array_address ) { + char *vec_base = static_cast (array_address); + char *heap_block = vec_base - padding_size; + st_heap_block2 heap ( dealloc, heap_block ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, __get_element_count ( vec_base ), + element_size, destructor ); + } +} + + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. The deallocation +// function takes both the object address and its size. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +_LIBCXXABI_FUNC_VIS void +__cxa_vec_delete3(void *array_address, size_t element_size, size_t padding_size, + void (*destructor)(void *), void (*dealloc)(void *, size_t)) { + if ( NULL != array_address ) { + char *vec_base = static_cast (array_address); + char *heap_block = vec_base - padding_size; + const size_t element_count = padding_size ? __get_element_count ( vec_base ) : 0; + const size_t heap_block_size = element_size * element_count + padding_size; + st_heap_block3 heap ( dealloc, heap_block, heap_block_size ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, element_count, element_size, destructor ); + } +} + + +} // extern "C" + +} // abi diff --git a/third_party/libcxxabi/cxa_virtual.cc b/third_party/libcxxabi/cxa_virtual.cc new file mode 100644 index 000000000..f054c241f --- /dev/null +++ b/third_party/libcxxabi/cxa_virtual.cc @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxxabi/include/cxxabi.h" +#include "third_party/libcxxabi/abort_message.h" + +namespace __cxxabiv1 { +extern "C" { +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN +void __cxa_pure_virtual(void) { + abort_message("Pure virtual function called!"); +} + +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN +void __cxa_deleted_virtual(void) { + abort_message("Deleted virtual function called!"); +} +} // extern "C" +} // abi diff --git a/third_party/libcxxabi/demangle/DemangleConfig.h b/third_party/libcxxabi/demangle/DemangleConfig.h new file mode 100644 index 000000000..bb09075f1 --- /dev/null +++ b/third_party/libcxxabi/demangle/DemangleConfig.h @@ -0,0 +1,105 @@ +//===--- DemangleConfig.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// This file is contains a subset of macros copied from +// llvm/include/llvm/Demangle/DemangleConfig.h +//===----------------------------------------------------------------------===// + +#ifndef LIBCXXABI_DEMANGLE_DEMANGLE_CONFIG_H +#define LIBCXXABI_DEMANGLE_DEMANGLE_CONFIG_H + +// Must be defined before pulling in headers from libc++. Allow downstream +// build systems to override this value. +// https://libcxx.llvm.org/UsingLibcxx.html#enabling-the-safe-libc-mode +#ifndef _LIBCPP_VERBOSE_ABORT +#define _LIBCPP_VERBOSE_ABORT(...) abort_message(__VA_ARGS__) +#include "third_party/libcxxabi/abort_message.h" +#endif + +#include "third_party/libcxx/ciso646" + +#ifdef _MSC_VER +// snprintf is implemented in VS 2015 +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef DEMANGLE_GNUC_PREREQ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ + ((maj) << 20) + ((min) << 10) + (patch)) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) +#else +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) 0 +#endif +#endif + +#if __has_attribute(used) || DEMANGLE_GNUC_PREREQ(3, 1, 0) +#define DEMANGLE_ATTRIBUTE_USED __attribute__((__used__)) +#else +#define DEMANGLE_ATTRIBUTE_USED +#endif + +#if __has_builtin(__builtin_unreachable) || DEMANGLE_GNUC_PREREQ(4, 5, 0) +#define DEMANGLE_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) +#define DEMANGLE_UNREACHABLE __assume(false) +#else +#define DEMANGLE_UNREACHABLE +#endif + +#if __has_attribute(noinline) || DEMANGLE_GNUC_PREREQ(3, 4, 0) +#define DEMANGLE_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define DEMANGLE_ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define DEMANGLE_ATTRIBUTE_NOINLINE +#endif + +#if !defined(NDEBUG) +#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE DEMANGLE_ATTRIBUTE_USED +#else +#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE +#endif + +#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough) +#define DEMANGLE_FALLTHROUGH [[fallthrough]] +#elif __has_cpp_attribute(gnu::fallthrough) +#define DEMANGLE_FALLTHROUGH [[gnu::fallthrough]] +#elif !__cplusplus +// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious +// error when __has_cpp_attribute is given a scoped attribute in C mode. +#define DEMANGLE_FALLTHROUGH +#elif __has_cpp_attribute(clang::fallthrough) +#define DEMANGLE_FALLTHROUGH [[clang::fallthrough]] +#else +#define DEMANGLE_FALLTHROUGH +#endif + +#define DEMANGLE_NAMESPACE_BEGIN namespace { namespace itanium_demangle { +#define DEMANGLE_NAMESPACE_END } } + +#endif // LIBCXXABI_DEMANGLE_DEMANGLE_CONFIG_H diff --git a/third_party/libcxxabi/demangle/ItaniumDemangle.h b/third_party/libcxxabi/demangle/ItaniumDemangle.h new file mode 100644 index 000000000..f2c2b2fde --- /dev/null +++ b/third_party/libcxxabi/demangle/ItaniumDemangle.h @@ -0,0 +1,5520 @@ +//===------------------------- ItaniumDemangle.h ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Generic itanium demangler library. +// There are two copies of this file in the source tree. The one under +// libcxxabi is the original and the one under llvm is the copy. Use +// cp-to-llvm.sh to update the copy. See README.txt for more details. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_ITANIUMDEMANGLE_H +#define DEMANGLE_ITANIUMDEMANGLE_H + +#include "third_party/libcxxabi/demangle/DemangleConfig.h" +#include "third_party/libcxxabi/demangle/StringViewExtras.h" +#include "third_party/libcxxabi/demangle/Utility.h" +#include "third_party/libcxxabi/include/__cxxabi_config.h" +#include "third_party/libcxx/algorithm" +#include "third_party/libcxx/cassert" +#include "third_party/libcxx/cctype" +#include "third_party/libcxx/cstdio" +#include "third_party/libcxx/cstdlib" +#include "third_party/libcxx/cstring" +#include "third_party/libcxx/limits" +#include "third_party/libcxx/new" +#include "third_party/libcxx/string_view" +#include "third_party/libcxx/type_traits" +#include "third_party/libcxx/utility" + +#ifdef _LIBCXXABI_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-template" +#endif + +DEMANGLE_NAMESPACE_BEGIN + +template class PODSmallVector { + static_assert(std::is_pod::value, + "T is required to be a plain old data type"); + + T *First = nullptr; + T *Last = nullptr; + T *Cap = nullptr; + T Inline[N] = {0}; + + bool isInline() const { return First == Inline; } + + void clearInline() { + First = Inline; + Last = Inline; + Cap = Inline + N; + } + + void reserve(size_t NewCap) { + size_t S = size(); + if (isInline()) { + auto *Tmp = static_cast(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) + std::terminate(); + std::copy(First, Last, Tmp); + First = Tmp; + } else { + First = static_cast(std::realloc(First, NewCap * sizeof(T))); + if (First == nullptr) + std::terminate(); + } + Last = First + S; + Cap = First + NewCap; + } + +public: + PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} + + PODSmallVector(const PODSmallVector &) = delete; + PODSmallVector &operator=(const PODSmallVector &) = delete; + + PODSmallVector(PODSmallVector &&Other) : PODSmallVector() { + if (Other.isInline()) { + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return; + } + + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + } + + PODSmallVector &operator=(PODSmallVector &&Other) { + if (Other.isInline()) { + if (!isInline()) { + std::free(First); + clearInline(); + } + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return *this; + } + + if (isInline()) { + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + return *this; + } + + std::swap(First, Other.First); + std::swap(Last, Other.Last); + std::swap(Cap, Other.Cap); + Other.clear(); + return *this; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void push_back(const T &Elem) { + if (Last == Cap) + reserve(size() * 2); + *Last++ = Elem; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void pop_back() { + assert(Last != First && "Popping empty vector!"); + --Last; + } + + void dropBack(size_t Index) { + assert(Index <= size() && "dropBack() can't expand!"); + Last = First + Index; + } + + T *begin() { return First; } + T *end() { return Last; } + + bool empty() const { return First == Last; } + size_t size() const { return static_cast(Last - First); } + T &back() { + assert(Last != First && "Calling back() on empty vector!"); + return *(Last - 1); + } + T &operator[](size_t Index) { + assert(Index < size() && "Invalid access!"); + return *(begin() + Index); + } + void clear() { Last = First; } + + ~PODSmallVector() { + if (!isInline()) + std::free(First); + } +}; + +// Base class of all AST nodes. The AST is built by the parser, then is +// traversed by the printLeft/Right functions to produce a demangled string. +class Node { +public: + enum Kind : unsigned char { +#define NODE(NodeKind) K##NodeKind, +#include "third_party/libcxxabi/demangle/ItaniumNodes.def" + }; + + /// Three-way bool to track a cached value. Unknown is possible if this node + /// has an unexpanded parameter pack below it that may affect this cache. + enum class Cache : unsigned char { Yes, No, Unknown, }; + + /// Operator precedence for expression nodes. Used to determine required + /// parens in expression emission. + enum class Prec { + Primary, + Postfix, + Unary, + Cast, + PtrMem, + Multiplicative, + Additive, + Shift, + Spaceship, + Relational, + Equality, + And, + Xor, + Ior, + AndIf, + OrIf, + Conditional, + Assign, + Comma, + Default, + }; + +private: + Kind K; + + Prec Precedence : 6; + + // FIXME: Make these protected. +public: + /// Tracks if this node has a component on its right side, in which case we + /// need to call printRight. + Cache RHSComponentCache : 2; + + /// Track if this node is a (possibly qualified) array type. This can affect + /// how we format the output string. + Cache ArrayCache : 2; + + /// Track if this node is a (possibly qualified) function type. This can + /// affect how we format the output string. + Cache FunctionCache : 2; + +public: + Node(Kind K_, Prec Precedence_ = Prec::Primary, + Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No) + : K(K_), Precedence(Precedence_), RHSComponentCache(RHSComponentCache_), + ArrayCache(ArrayCache_), FunctionCache(FunctionCache_) {} + Node(Kind K_, Cache RHSComponentCache_, Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No) + : Node(K_, Prec::Primary, RHSComponentCache_, ArrayCache_, + FunctionCache_) {} + + /// Visit the most-derived object corresponding to this object. + template void visit(Fn F) const; + + // The following function is provided by all derived classes: + // + // Call F with arguments that, when passed to the constructor of this node, + // would construct an equivalent node. + //template void match(Fn F) const; + + bool hasRHSComponent(OutputBuffer &OB) const { + if (RHSComponentCache != Cache::Unknown) + return RHSComponentCache == Cache::Yes; + return hasRHSComponentSlow(OB); + } + + bool hasArray(OutputBuffer &OB) const { + if (ArrayCache != Cache::Unknown) + return ArrayCache == Cache::Yes; + return hasArraySlow(OB); + } + + bool hasFunction(OutputBuffer &OB) const { + if (FunctionCache != Cache::Unknown) + return FunctionCache == Cache::Yes; + return hasFunctionSlow(OB); + } + + Kind getKind() const { return K; } + + Prec getPrecedence() const { return Precedence; } + + virtual bool hasRHSComponentSlow(OutputBuffer &) const { return false; } + virtual bool hasArraySlow(OutputBuffer &) const { return false; } + virtual bool hasFunctionSlow(OutputBuffer &) const { return false; } + + // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to + // get at a node that actually represents some concrete syntax. + virtual const Node *getSyntaxNode(OutputBuffer &) const { return this; } + + // Print this node as an expression operand, surrounding it in parentheses if + // its precedence is [Strictly] weaker than P. + void printAsOperand(OutputBuffer &OB, Prec P = Prec::Default, + bool StrictlyWorse = false) const { + bool Paren = + unsigned(getPrecedence()) >= unsigned(P) + unsigned(StrictlyWorse); + if (Paren) + OB.printOpen(); + print(OB); + if (Paren) + OB.printClose(); + } + + void print(OutputBuffer &OB) const { + printLeft(OB); + if (RHSComponentCache != Cache::No) + printRight(OB); + } + + // Print the "left" side of this Node into OutputBuffer. + virtual void printLeft(OutputBuffer &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + virtual void printRight(OutputBuffer &) const {} + + virtual std::string_view getBaseName() const { return {}; } + + // Silence compiler warnings, this dtor will never be called. + virtual ~Node() = default; + +#ifndef NDEBUG + DEMANGLE_DUMP_METHOD void dump() const; +#endif +}; + +class NodeArray { + Node **Elements; + size_t NumElements; + +public: + NodeArray() : Elements(nullptr), NumElements(0) {} + NodeArray(Node **Elements_, size_t NumElements_) + : Elements(Elements_), NumElements(NumElements_) {} + + bool empty() const { return NumElements == 0; } + size_t size() const { return NumElements; } + + Node **begin() const { return Elements; } + Node **end() const { return Elements + NumElements; } + + Node *operator[](size_t Idx) const { return Elements[Idx]; } + + void printWithComma(OutputBuffer &OB) const { + bool FirstElement = true; + for (size_t Idx = 0; Idx != NumElements; ++Idx) { + size_t BeforeComma = OB.getCurrentPosition(); + if (!FirstElement) + OB += ", "; + size_t AfterComma = OB.getCurrentPosition(); + Elements[Idx]->printAsOperand(OB, Node::Prec::Comma); + + // Elements[Idx] is an empty parameter pack expansion, we should erase the + // comma we just printed. + if (AfterComma == OB.getCurrentPosition()) { + OB.setCurrentPosition(BeforeComma); + continue; + } + + FirstElement = false; + } + } +}; + +struct NodeArrayNode : Node { + NodeArray Array; + NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {} + + template void match(Fn F) const { F(Array); } + + void printLeft(OutputBuffer &OB) const override { Array.printWithComma(OB); } +}; + +class DotSuffix final : public Node { + const Node *Prefix; + const std::string_view Suffix; + +public: + DotSuffix(const Node *Prefix_, std::string_view Suffix_) + : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {} + + template void match(Fn F) const { F(Prefix, Suffix); } + + void printLeft(OutputBuffer &OB) const override { + Prefix->print(OB); + OB += " ("; + OB += Suffix; + OB += ")"; + } +}; + +class VendorExtQualType final : public Node { + const Node *Ty; + std::string_view Ext; + const Node *TA; + +public: + VendorExtQualType(const Node *Ty_, std::string_view Ext_, const Node *TA_) + : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_), TA(TA_) {} + + const Node *getTy() const { return Ty; } + std::string_view getExt() const { return Ext; } + const Node *getTA() const { return TA; } + + template void match(Fn F) const { F(Ty, Ext, TA); } + + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += " "; + OB += Ext; + if (TA != nullptr) + TA->print(OB); + } +}; + +enum FunctionRefQual : unsigned char { + FrefQualNone, + FrefQualLValue, + FrefQualRValue, +}; + +enum Qualifiers { + QualNone = 0, + QualConst = 0x1, + QualVolatile = 0x2, + QualRestrict = 0x4, +}; + +inline Qualifiers operator|=(Qualifiers &Q1, Qualifiers Q2) { + return Q1 = static_cast(Q1 | Q2); +} + +class QualType final : public Node { +protected: + const Qualifiers Quals; + const Node *Child; + + void printQuals(OutputBuffer &OB) const { + if (Quals & QualConst) + OB += " const"; + if (Quals & QualVolatile) + OB += " volatile"; + if (Quals & QualRestrict) + OB += " restrict"; + } + +public: + QualType(const Node *Child_, Qualifiers Quals_) + : Node(KQualType, Child_->RHSComponentCache, + Child_->ArrayCache, Child_->FunctionCache), + Quals(Quals_), Child(Child_) {} + + Qualifiers getQuals() const { return Quals; } + const Node *getChild() const { return Child; } + + template void match(Fn F) const { F(Child, Quals); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Child->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer &OB) const override { + return Child->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer &OB) const override { + return Child->hasFunction(OB); + } + + void printLeft(OutputBuffer &OB) const override { + Child->printLeft(OB); + printQuals(OB); + } + + void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } +}; + +class ConversionOperatorType final : public Node { + const Node *Ty; + +public: + ConversionOperatorType(const Node *Ty_) + : Node(KConversionOperatorType), Ty(Ty_) {} + + template void match(Fn F) const { F(Ty); } + + void printLeft(OutputBuffer &OB) const override { + OB += "operator "; + Ty->print(OB); + } +}; + +class PostfixQualifiedType final : public Node { + const Node *Ty; + const std::string_view Postfix; + +public: + PostfixQualifiedType(const Node *Ty_, std::string_view Postfix_) + : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} + + template void match(Fn F) const { F(Ty, Postfix); } + + void printLeft(OutputBuffer &OB) const override { + Ty->printLeft(OB); + OB += Postfix; + } +}; + +class NameType final : public Node { + const std::string_view Name; + +public: + NameType(std::string_view Name_) : Node(KNameType), Name(Name_) {} + + template void match(Fn F) const { F(Name); } + + std::string_view getName() const { return Name; } + std::string_view getBaseName() const override { return Name; } + + void printLeft(OutputBuffer &OB) const override { OB += Name; } +}; + +class BitIntType final : public Node { + const Node *Size; + bool Signed; + +public: + BitIntType(const Node *Size_, bool Signed_) + : Node(KBitIntType), Size(Size_), Signed(Signed_) {} + + template void match(Fn F) const { F(Size, Signed); } + + void printLeft(OutputBuffer &OB) const override { + if (!Signed) + OB += "unsigned "; + OB += "_BitInt"; + OB.printOpen(); + Size->printAsOperand(OB); + OB.printClose(); + } +}; + +class ElaboratedTypeSpefType : public Node { + std::string_view Kind; + Node *Child; +public: + ElaboratedTypeSpefType(std::string_view Kind_, Node *Child_) + : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {} + + template void match(Fn F) const { F(Kind, Child); } + + void printLeft(OutputBuffer &OB) const override { + OB += Kind; + OB += ' '; + Child->print(OB); + } +}; + +struct AbiTagAttr : Node { + Node *Base; + std::string_view Tag; + + AbiTagAttr(Node *Base_, std::string_view Tag_) + : Node(KAbiTagAttr, Base_->RHSComponentCache, Base_->ArrayCache, + Base_->FunctionCache), + Base(Base_), Tag(Tag_) {} + + template void match(Fn F) const { F(Base, Tag); } + + std::string_view getBaseName() const override { return Base->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Base->printLeft(OB); + OB += "[abi:"; + OB += Tag; + OB += "]"; + } +}; + +class EnableIfAttr : public Node { + NodeArray Conditions; +public: + EnableIfAttr(NodeArray Conditions_) + : Node(KEnableIfAttr), Conditions(Conditions_) {} + + template void match(Fn F) const { F(Conditions); } + + void printLeft(OutputBuffer &OB) const override { + OB += " [enable_if:"; + Conditions.printWithComma(OB); + OB += ']'; + } +}; + +class ObjCProtoName : public Node { + const Node *Ty; + std::string_view Protocol; + + friend class PointerType; + +public: + ObjCProtoName(const Node *Ty_, std::string_view Protocol_) + : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} + + template void match(Fn F) const { F(Ty, Protocol); } + + bool isObjCObject() const { + return Ty->getKind() == KNameType && + static_cast(Ty)->getName() == "objc_object"; + } + + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += "<"; + OB += Protocol; + OB += ">"; + } +}; + +class PointerType final : public Node { + const Node *Pointee; + +public: + PointerType(const Node *Pointee_) + : Node(KPointerType, Pointee_->RHSComponentCache), + Pointee(Pointee_) {} + + const Node *getPointee() const { return Pointee; } + + template void match(Fn F) const { F(Pointee); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); + } + + void printLeft(OutputBuffer &OB) const override { + // We rewrite objc_object* into id. + if (Pointee->getKind() != KObjCProtoName || + !static_cast(Pointee)->isObjCObject()) { + Pointee->printLeft(OB); + if (Pointee->hasArray(OB)) + OB += " "; + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += "("; + OB += "*"; + } else { + const auto *objcProto = static_cast(Pointee); + OB += "id<"; + OB += objcProto->Protocol; + OB += ">"; + } + } + + void printRight(OutputBuffer &OB) const override { + if (Pointee->getKind() != KObjCProtoName || + !static_cast(Pointee)->isObjCObject()) { + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += ")"; + Pointee->printRight(OB); + } + } +}; + +enum class ReferenceKind { + LValue, + RValue, +}; + +// Represents either a LValue or an RValue reference type. +class ReferenceType : public Node { + const Node *Pointee; + ReferenceKind RK; + + mutable bool Printing = false; + + // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The + // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any + // other combination collapses to a lvalue ref. + // + // A combination of a TemplateForwardReference and a back-ref Substitution + // from an ill-formed string may have created a cycle; use cycle detection to + // avoid looping forever. + std::pair collapse(OutputBuffer &OB) const { + auto SoFar = std::make_pair(RK, Pointee); + // Track the chain of nodes for the Floyd's 'tortoise and hare' + // cycle-detection algorithm, since getSyntaxNode(S) is impure + PODSmallVector Prev; + for (;;) { + const Node *SN = SoFar.second->getSyntaxNode(OB); + if (SN->getKind() != KReferenceType) + break; + auto *RT = static_cast(SN); + SoFar.second = RT->Pointee; + SoFar.first = std::min(SoFar.first, RT->RK); + + // The middle of Prev is the 'slow' pointer moving at half speed + Prev.push_back(SoFar.second); + if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) { + // Cycle detected + SoFar.second = nullptr; + break; + } + } + return SoFar; + } + +public: + ReferenceType(const Node *Pointee_, ReferenceKind RK_) + : Node(KReferenceType, Pointee_->RHSComponentCache), + Pointee(Pointee_), RK(RK_) {} + + template void match(Fn F) const { F(Pointee, RK); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); + } + + void printLeft(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride SavePrinting(Printing, true); + std::pair Collapsed = collapse(OB); + if (!Collapsed.second) + return; + Collapsed.second->printLeft(OB); + if (Collapsed.second->hasArray(OB)) + OB += " "; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += "("; + + OB += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + } + void printRight(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride SavePrinting(Printing, true); + std::pair Collapsed = collapse(OB); + if (!Collapsed.second) + return; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += ")"; + Collapsed.second->printRight(OB); + } +}; + +class PointerToMemberType final : public Node { + const Node *ClassType; + const Node *MemberType; + +public: + PointerToMemberType(const Node *ClassType_, const Node *MemberType_) + : Node(KPointerToMemberType, MemberType_->RHSComponentCache), + ClassType(ClassType_), MemberType(MemberType_) {} + + template void match(Fn F) const { F(ClassType, MemberType); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return MemberType->hasRHSComponent(OB); + } + + void printLeft(OutputBuffer &OB) const override { + MemberType->printLeft(OB); + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += "("; + else + OB += " "; + ClassType->print(OB); + OB += "::*"; + } + + void printRight(OutputBuffer &OB) const override { + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += ")"; + MemberType->printRight(OB); + } +}; + +class ArrayType final : public Node { + const Node *Base; + Node *Dimension; + +public: + ArrayType(const Node *Base_, Node *Dimension_) + : Node(KArrayType, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::Yes), + Base(Base_), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Base, Dimension); } + + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasArraySlow(OutputBuffer &) const override { return true; } + + void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } + + void printRight(OutputBuffer &OB) const override { + if (OB.back() != ']') + OB += " "; + OB += "["; + if (Dimension) + Dimension->print(OB); + OB += "]"; + Base->printRight(OB); + } +}; + +class FunctionType final : public Node { + const Node *Ret; + NodeArray Params; + Qualifiers CVQuals; + FunctionRefQual RefQual; + const Node *ExceptionSpec; + +public: + FunctionType(const Node *Ret_, NodeArray Params_, Qualifiers CVQuals_, + FunctionRefQual RefQual_, const Node *ExceptionSpec_) + : Node(KFunctionType, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_), + ExceptionSpec(ExceptionSpec_) {} + + template void match(Fn F) const { + F(Ret, Params, CVQuals, RefQual, ExceptionSpec); + } + + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) const override { return true; } + + // Handle C++'s ... quirky decl grammar by using the left & right + // distinction. Consider: + // int (*f(float))(char) {} + // f is a function that takes a float and returns a pointer to a function + // that takes a char and returns an int. If we're trying to print f, start + // by printing out the return types's left, then print our parameters, then + // finally print right of the return type. + void printLeft(OutputBuffer &OB) const override { + Ret->printLeft(OB); + OB += " "; + } + + void printRight(OutputBuffer &OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + Ret->printRight(OB); + + if (CVQuals & QualConst) + OB += " const"; + if (CVQuals & QualVolatile) + OB += " volatile"; + if (CVQuals & QualRestrict) + OB += " restrict"; + + if (RefQual == FrefQualLValue) + OB += " &"; + else if (RefQual == FrefQualRValue) + OB += " &&"; + + if (ExceptionSpec != nullptr) { + OB += ' '; + ExceptionSpec->print(OB); + } + } +}; + +class NoexceptSpec : public Node { + const Node *E; +public: + NoexceptSpec(const Node *E_) : Node(KNoexceptSpec), E(E_) {} + + template void match(Fn F) const { F(E); } + + void printLeft(OutputBuffer &OB) const override { + OB += "noexcept"; + OB.printOpen(); + E->printAsOperand(OB); + OB.printClose(); + } +}; + +class DynamicExceptionSpec : public Node { + NodeArray Types; +public: + DynamicExceptionSpec(NodeArray Types_) + : Node(KDynamicExceptionSpec), Types(Types_) {} + + template void match(Fn F) const { F(Types); } + + void printLeft(OutputBuffer &OB) const override { + OB += "throw"; + OB.printOpen(); + Types.printWithComma(OB); + OB.printClose(); + } +}; + +class FunctionEncoding final : public Node { + const Node *Ret; + const Node *Name; + NodeArray Params; + const Node *Attrs; + Qualifiers CVQuals; + FunctionRefQual RefQual; + +public: + FunctionEncoding(const Node *Ret_, const Node *Name_, NodeArray Params_, + const Node *Attrs_, Qualifiers CVQuals_, + FunctionRefQual RefQual_) + : Node(KFunctionEncoding, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_), + CVQuals(CVQuals_), RefQual(RefQual_) {} + + template void match(Fn F) const { + F(Ret, Name, Params, Attrs, CVQuals, RefQual); + } + + Qualifiers getCVQuals() const { return CVQuals; } + FunctionRefQual getRefQual() const { return RefQual; } + NodeArray getParams() const { return Params; } + const Node *getReturnType() const { return Ret; } + + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) const override { return true; } + + const Node *getName() const { return Name; } + + void printLeft(OutputBuffer &OB) const override { + if (Ret) { + Ret->printLeft(OB); + if (!Ret->hasRHSComponent(OB)) + OB += " "; + } + Name->print(OB); + } + + void printRight(OutputBuffer &OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + if (Ret) + Ret->printRight(OB); + + if (CVQuals & QualConst) + OB += " const"; + if (CVQuals & QualVolatile) + OB += " volatile"; + if (CVQuals & QualRestrict) + OB += " restrict"; + + if (RefQual == FrefQualLValue) + OB += " &"; + else if (RefQual == FrefQualRValue) + OB += " &&"; + + if (Attrs != nullptr) + Attrs->print(OB); + } +}; + +class LiteralOperator : public Node { + const Node *OpName; + +public: + LiteralOperator(const Node *OpName_) + : Node(KLiteralOperator), OpName(OpName_) {} + + template void match(Fn F) const { F(OpName); } + + void printLeft(OutputBuffer &OB) const override { + OB += "operator\"\" "; + OpName->print(OB); + } +}; + +class SpecialName final : public Node { + const std::string_view Special; + const Node *Child; + +public: + SpecialName(std::string_view Special_, const Node *Child_) + : Node(KSpecialName), Special(Special_), Child(Child_) {} + + template void match(Fn F) const { F(Special, Child); } + + void printLeft(OutputBuffer &OB) const override { + OB += Special; + Child->print(OB); + } +}; + +class CtorVtableSpecialName final : public Node { + const Node *FirstType; + const Node *SecondType; + +public: + CtorVtableSpecialName(const Node *FirstType_, const Node *SecondType_) + : Node(KCtorVtableSpecialName), + FirstType(FirstType_), SecondType(SecondType_) {} + + template void match(Fn F) const { F(FirstType, SecondType); } + + void printLeft(OutputBuffer &OB) const override { + OB += "construction vtable for "; + FirstType->print(OB); + OB += "-in-"; + SecondType->print(OB); + } +}; + +struct NestedName : Node { + Node *Qual; + Node *Name; + + NestedName(Node *Qual_, Node *Name_) + : Node(KNestedName), Qual(Qual_), Name(Name_) {} + + template void match(Fn F) const { F(Qual, Name); } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Qual->print(OB); + OB += "::"; + Name->print(OB); + } +}; + +struct ModuleName : Node { + ModuleName *Parent; + Node *Name; + bool IsPartition; + + ModuleName(ModuleName *Parent_, Node *Name_, bool IsPartition_ = false) + : Node(KModuleName), Parent(Parent_), Name(Name_), + IsPartition(IsPartition_) {} + + template void match(Fn F) const { + F(Parent, Name, IsPartition); + } + + void printLeft(OutputBuffer &OB) const override { + if (Parent) + Parent->print(OB); + if (Parent || IsPartition) + OB += IsPartition ? ':' : '.'; + Name->print(OB); + } +}; + +struct ModuleEntity : Node { + ModuleName *Module; + Node *Name; + + ModuleEntity(ModuleName *Module_, Node *Name_) + : Node(KModuleEntity), Module(Module_), Name(Name_) {} + + template void match(Fn F) const { F(Module, Name); } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Name->print(OB); + OB += '@'; + Module->print(OB); + } +}; + +struct LocalName : Node { + Node *Encoding; + Node *Entity; + + LocalName(Node *Encoding_, Node *Entity_) + : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {} + + template void match(Fn F) const { F(Encoding, Entity); } + + void printLeft(OutputBuffer &OB) const override { + Encoding->print(OB); + OB += "::"; + Entity->print(OB); + } +}; + +class QualifiedName final : public Node { + // qualifier::name + const Node *Qualifier; + const Node *Name; + +public: + QualifiedName(const Node *Qualifier_, const Node *Name_) + : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {} + + template void match(Fn F) const { F(Qualifier, Name); } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Qualifier->print(OB); + OB += "::"; + Name->print(OB); + } +}; + +class VectorType final : public Node { + const Node *BaseType; + const Node *Dimension; + +public: + VectorType(const Node *BaseType_, const Node *Dimension_) + : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_) {} + + const Node *getBaseType() const { return BaseType; } + const Node *getDimension() const { return Dimension; } + + template void match(Fn F) const { F(BaseType, Dimension); } + + void printLeft(OutputBuffer &OB) const override { + BaseType->print(OB); + OB += " vector["; + if (Dimension) + Dimension->print(OB); + OB += "]"; + } +}; + +class PixelVectorType final : public Node { + const Node *Dimension; + +public: + PixelVectorType(const Node *Dimension_) + : Node(KPixelVectorType), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Dimension); } + + void printLeft(OutputBuffer &OB) const override { + // FIXME: This should demangle as "vector pixel". + OB += "pixel vector["; + Dimension->print(OB); + OB += "]"; + } +}; + +class BinaryFPType final : public Node { + const Node *Dimension; + +public: + BinaryFPType(const Node *Dimension_) + : Node(KBinaryFPType), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Dimension); } + + void printLeft(OutputBuffer &OB) const override { + OB += "_Float"; + Dimension->print(OB); + } +}; + +enum class TemplateParamKind { Type, NonType, Template }; + +/// An invented name for a template parameter for which we don't have a +/// corresponding template argument. +/// +/// This node is created when parsing the for a lambda with +/// explicit template arguments, which might be referenced in the parameter +/// types appearing later in the . +class SyntheticTemplateParamName final : public Node { + TemplateParamKind Kind; + unsigned Index; + +public: + SyntheticTemplateParamName(TemplateParamKind Kind_, unsigned Index_) + : Node(KSyntheticTemplateParamName), Kind(Kind_), Index(Index_) {} + + template void match(Fn F) const { F(Kind, Index); } + + void printLeft(OutputBuffer &OB) const override { + switch (Kind) { + case TemplateParamKind::Type: + OB += "$T"; + break; + case TemplateParamKind::NonType: + OB += "$N"; + break; + case TemplateParamKind::Template: + OB += "$TT"; + break; + } + if (Index > 0) + OB << Index - 1; + } +}; + +/// A template type parameter declaration, 'typename T'. +class TypeTemplateParamDecl final : public Node { + Node *Name; + +public: + TypeTemplateParamDecl(Node *Name_) + : Node(KTypeTemplateParamDecl, Cache::Yes), Name(Name_) {} + + template void match(Fn F) const { F(Name); } + + void printLeft(OutputBuffer &OB) const override { OB += "typename "; } + + void printRight(OutputBuffer &OB) const override { Name->print(OB); } +}; + +/// A non-type template parameter declaration, 'int N'. +class NonTypeTemplateParamDecl final : public Node { + Node *Name; + Node *Type; + +public: + NonTypeTemplateParamDecl(Node *Name_, Node *Type_) + : Node(KNonTypeTemplateParamDecl, Cache::Yes), Name(Name_), Type(Type_) {} + + template void match(Fn F) const { F(Name, Type); } + + void printLeft(OutputBuffer &OB) const override { + Type->printLeft(OB); + if (!Type->hasRHSComponent(OB)) + OB += " "; + } + + void printRight(OutputBuffer &OB) const override { + Name->print(OB); + Type->printRight(OB); + } +}; + +/// A template template parameter declaration, +/// 'template typename N'. +class TemplateTemplateParamDecl final : public Node { + Node *Name; + NodeArray Params; + +public: + TemplateTemplateParamDecl(Node *Name_, NodeArray Params_) + : Node(KTemplateTemplateParamDecl, Cache::Yes), Name(Name_), + Params(Params_) {} + + template void match(Fn F) const { F(Name, Params); } + + void printLeft(OutputBuffer &OB) const override { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "template<"; + Params.printWithComma(OB); + OB += "> typename "; + } + + void printRight(OutputBuffer &OB) const override { Name->print(OB); } +}; + +/// A template parameter pack declaration, 'typename ...T'. +class TemplateParamPackDecl final : public Node { + Node *Param; + +public: + TemplateParamPackDecl(Node *Param_) + : Node(KTemplateParamPackDecl, Cache::Yes), Param(Param_) {} + + template void match(Fn F) const { F(Param); } + + void printLeft(OutputBuffer &OB) const override { + Param->printLeft(OB); + OB += "..."; + } + + void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } +}; + +/// An unexpanded parameter pack (either in the expression or type context). If +/// this AST is correct, this node will have a ParameterPackExpansion node above +/// it. +/// +/// This node is created when some are found that apply to an +/// , and is stored in the TemplateParams table. In order for this to +/// appear in the final AST, it has to referenced via a (ie, +/// T_). +class ParameterPack final : public Node { + NodeArray Data; + + // Setup OutputBuffer for a pack expansion, unless we're already expanding + // one. + void initializePackExpansion(OutputBuffer &OB) const { + if (OB.CurrentPackMax == std::numeric_limits::max()) { + OB.CurrentPackMax = static_cast(Data.size()); + OB.CurrentPackIndex = 0; + } + } + +public: + ParameterPack(NodeArray Data_) : Node(KParameterPack), Data(Data_) { + ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->ArrayCache == Cache::No; + })) + ArrayCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->FunctionCache == Cache::No; + })) + FunctionCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->RHSComponentCache == Cache::No; + })) + RHSComponentCache = Cache::No; + } + + template void match(Fn F) const { F(Data); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(OB); + } + const Node *getSyntaxNode(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(OB) : this; + } + + void printLeft(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printLeft(OB); + } + void printRight(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printRight(OB); + } +}; + +/// A variadic template argument. This node represents an occurrence of +/// JE in some . It isn't itself unexpanded, unless +/// one of it's Elements is. The parser inserts a ParameterPack into the +/// TemplateParams table if the this pack belongs to apply to an +/// . +class TemplateArgumentPack final : public Node { + NodeArray Elements; +public: + TemplateArgumentPack(NodeArray Elements_) + : Node(KTemplateArgumentPack), Elements(Elements_) {} + + template void match(Fn F) const { F(Elements); } + + NodeArray getElements() const { return Elements; } + + void printLeft(OutputBuffer &OB) const override { + Elements.printWithComma(OB); + } +}; + +/// A pack expansion. Below this node, there are some unexpanded ParameterPacks +/// which each have Child->ParameterPackSize elements. +class ParameterPackExpansion final : public Node { + const Node *Child; + +public: + ParameterPackExpansion(const Node *Child_) + : Node(KParameterPackExpansion), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + const Node *getChild() const { return Child; } + + void printLeft(OutputBuffer &OB) const override { + constexpr unsigned Max = std::numeric_limits::max(); + ScopedOverride SavePackIdx(OB.CurrentPackIndex, Max); + ScopedOverride SavePackMax(OB.CurrentPackMax, Max); + size_t StreamPos = OB.getCurrentPosition(); + + // Print the first element in the pack. If Child contains a ParameterPack, + // it will set up S.CurrentPackMax and print the first element. + Child->print(OB); + + // No ParameterPack was found in Child. This can occur if we've found a pack + // expansion on a . + if (OB.CurrentPackMax == Max) { + OB += "..."; + return; + } + + // We found a ParameterPack, but it has no elements. Erase whatever we may + // of printed. + if (OB.CurrentPackMax == 0) { + OB.setCurrentPosition(StreamPos); + return; + } + + // Else, iterate through the rest of the elements in the pack. + for (unsigned I = 1, E = OB.CurrentPackMax; I < E; ++I) { + OB += ", "; + OB.CurrentPackIndex = I; + Child->print(OB); + } + } +}; + +class TemplateArgs final : public Node { + NodeArray Params; + +public: + TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {} + + template void match(Fn F) const { F(Params); } + + NodeArray getParams() { return Params; } + + void printLeft(OutputBuffer &OB) const override { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "<"; + Params.printWithComma(OB); + OB += ">"; + } +}; + +/// A forward-reference to a template argument that was not known at the point +/// where the template parameter name was parsed in a mangling. +/// +/// This is created when demangling the name of a specialization of a +/// conversion function template: +/// +/// \code +/// struct A { +/// template operator T*(); +/// }; +/// \endcode +/// +/// When demangling a specialization of the conversion function template, we +/// encounter the name of the template (including the \c T) before we reach +/// the template argument list, so we cannot substitute the parameter name +/// for the corresponding argument while parsing. Instead, we create a +/// \c ForwardTemplateReference node that is resolved after we parse the +/// template arguments. +struct ForwardTemplateReference : Node { + size_t Index; + Node *Ref = nullptr; + + // If we're currently printing this node. It is possible (though invalid) for + // a forward template reference to refer to itself via a substitution. This + // creates a cyclic AST, which will stack overflow printing. To fix this, bail + // out if more than one print* function is active. + mutable bool Printing = false; + + ForwardTemplateReference(size_t Index_) + : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown, + Cache::Unknown), + Index(Index_) {} + + // We don't provide a matcher for these, because the value of the node is + // not determined by its construction parameters, and it generally needs + // special handling. + template void match(Fn F) const = delete; + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + if (Printing) + return false; + ScopedOverride SavePrinting(Printing, true); + return Ref->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer &OB) const override { + if (Printing) + return false; + ScopedOverride SavePrinting(Printing, true); + return Ref->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer &OB) const override { + if (Printing) + return false; + ScopedOverride SavePrinting(Printing, true); + return Ref->hasFunction(OB); + } + const Node *getSyntaxNode(OutputBuffer &OB) const override { + if (Printing) + return this; + ScopedOverride SavePrinting(Printing, true); + return Ref->getSyntaxNode(OB); + } + + void printLeft(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride SavePrinting(Printing, true); + Ref->printLeft(OB); + } + void printRight(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride SavePrinting(Printing, true); + Ref->printRight(OB); + } +}; + +struct NameWithTemplateArgs : Node { + // name + Node *Name; + Node *TemplateArgs; + + NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) + : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {} + + template void match(Fn F) const { F(Name, TemplateArgs); } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Name->print(OB); + TemplateArgs->print(OB); + } +}; + +class GlobalQualifiedName final : public Node { + Node *Child; + +public: + GlobalQualifiedName(Node* Child_) + : Node(KGlobalQualifiedName), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + std::string_view getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + OB += "::"; + Child->print(OB); + } +}; + +enum class SpecialSubKind { + allocator, + basic_string, + string, + istream, + ostream, + iostream, +}; + +class SpecialSubstitution; +class ExpandedSpecialSubstitution : public Node { +protected: + SpecialSubKind SSK; + + ExpandedSpecialSubstitution(SpecialSubKind SSK_, Kind K_) + : Node(K_), SSK(SSK_) {} +public: + ExpandedSpecialSubstitution(SpecialSubKind SSK_) + : ExpandedSpecialSubstitution(SSK_, KExpandedSpecialSubstitution) {} + inline ExpandedSpecialSubstitution(SpecialSubstitution const *); + + template void match(Fn F) const { F(SSK); } + +protected: + bool isInstantiation() const { + return unsigned(SSK) >= unsigned(SpecialSubKind::string); + } + + std::string_view getBaseName() const override { + switch (SSK) { + case SpecialSubKind::allocator: + return {"allocator"}; + case SpecialSubKind::basic_string: + return {"basic_string"}; + case SpecialSubKind::string: + return {"basic_string"}; + case SpecialSubKind::istream: + return {"basic_istream"}; + case SpecialSubKind::ostream: + return {"basic_ostream"}; + case SpecialSubKind::iostream: + return {"basic_iostream"}; + } + DEMANGLE_UNREACHABLE; + } + +private: + void printLeft(OutputBuffer &OB) const override { + OB << "std::" << getBaseName(); + if (isInstantiation()) { + OB << ""; + if (SSK == SpecialSubKind::string) + OB << ", std::allocator"; + OB << ">"; + } + } +}; + +class SpecialSubstitution final : public ExpandedSpecialSubstitution { +public: + SpecialSubstitution(SpecialSubKind SSK_) + : ExpandedSpecialSubstitution(SSK_, KSpecialSubstitution) {} + + template void match(Fn F) const { F(SSK); } + + std::string_view getBaseName() const override { + std::string_view SV = ExpandedSpecialSubstitution::getBaseName(); + if (isInstantiation()) { + // The instantiations are typedefs that drop the "basic_" prefix. + assert(starts_with(SV, "basic_")); + SV.remove_prefix(sizeof("basic_") - 1); + } + return SV; + } + + void printLeft(OutputBuffer &OB) const override { + OB << "std::" << getBaseName(); + } +}; + +inline ExpandedSpecialSubstitution::ExpandedSpecialSubstitution( + SpecialSubstitution const *SS) + : ExpandedSpecialSubstitution(SS->SSK) {} + +class CtorDtorName final : public Node { + const Node *Basename; + const bool IsDtor; + const int Variant; + +public: + CtorDtorName(const Node *Basename_, bool IsDtor_, int Variant_) + : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_), + Variant(Variant_) {} + + template void match(Fn F) const { F(Basename, IsDtor, Variant); } + + void printLeft(OutputBuffer &OB) const override { + if (IsDtor) + OB += "~"; + OB += Basename->getBaseName(); + } +}; + +class DtorName : public Node { + const Node *Base; + +public: + DtorName(const Node *Base_) : Node(KDtorName), Base(Base_) {} + + template void match(Fn F) const { F(Base); } + + void printLeft(OutputBuffer &OB) const override { + OB += "~"; + Base->printLeft(OB); + } +}; + +class UnnamedTypeName : public Node { + const std::string_view Count; + +public: + UnnamedTypeName(std::string_view Count_) + : Node(KUnnamedTypeName), Count(Count_) {} + + template void match(Fn F) const { F(Count); } + + void printLeft(OutputBuffer &OB) const override { + OB += "'unnamed"; + OB += Count; + OB += "\'"; + } +}; + +class ClosureTypeName : public Node { + NodeArray TemplateParams; + NodeArray Params; + std::string_view Count; + +public: + ClosureTypeName(NodeArray TemplateParams_, NodeArray Params_, + std::string_view Count_) + : Node(KClosureTypeName), TemplateParams(TemplateParams_), + Params(Params_), Count(Count_) {} + + template void match(Fn F) const { + F(TemplateParams, Params, Count); + } + + void printDeclarator(OutputBuffer &OB) const { + if (!TemplateParams.empty()) { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "<"; + TemplateParams.printWithComma(OB); + OB += ">"; + } + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + } + + void printLeft(OutputBuffer &OB) const override { + OB += "\'lambda"; + OB += Count; + OB += "\'"; + printDeclarator(OB); + } +}; + +class StructuredBindingName : public Node { + NodeArray Bindings; +public: + StructuredBindingName(NodeArray Bindings_) + : Node(KStructuredBindingName), Bindings(Bindings_) {} + + template void match(Fn F) const { F(Bindings); } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen('['); + Bindings.printWithComma(OB); + OB.printClose(']'); + } +}; + +// -- Expression Nodes -- + +class BinaryExpr : public Node { + const Node *LHS; + const std::string_view InfixOperator; + const Node *RHS; + +public: + BinaryExpr(const Node *LHS_, std::string_view InfixOperator_, + const Node *RHS_, Prec Prec_) + : Node(KBinaryExpr, Prec_), LHS(LHS_), InfixOperator(InfixOperator_), + RHS(RHS_) {} + + template void match(Fn F) const { + F(LHS, InfixOperator, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + bool ParenAll = OB.isGtInsideTemplateArgs() && + (InfixOperator == ">" || InfixOperator == ">>"); + if (ParenAll) + OB.printOpen(); + // Assignment is right associative, with special LHS precedence. + bool IsAssign = getPrecedence() == Prec::Assign; + LHS->printAsOperand(OB, IsAssign ? Prec::OrIf : getPrecedence(), !IsAssign); + // No space before comma operator + if (!(InfixOperator == ",")) + OB += " "; + OB += InfixOperator; + OB += " "; + RHS->printAsOperand(OB, getPrecedence(), IsAssign); + if (ParenAll) + OB.printClose(); + } +}; + +class ArraySubscriptExpr : public Node { + const Node *Op1; + const Node *Op2; + +public: + ArraySubscriptExpr(const Node *Op1_, const Node *Op2_, Prec Prec_) + : Node(KArraySubscriptExpr, Prec_), Op1(Op1_), Op2(Op2_) {} + + template void match(Fn F) const { + F(Op1, Op2, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Op1->printAsOperand(OB, getPrecedence()); + OB.printOpen('['); + Op2->printAsOperand(OB); + OB.printClose(']'); + } +}; + +class PostfixExpr : public Node { + const Node *Child; + const std::string_view Operator; + +public: + PostfixExpr(const Node *Child_, std::string_view Operator_, Prec Prec_) + : Node(KPostfixExpr, Prec_), Child(Child_), Operator(Operator_) {} + + template void match(Fn F) const { + F(Child, Operator, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Child->printAsOperand(OB, getPrecedence(), true); + OB += Operator; + } +}; + +class ConditionalExpr : public Node { + const Node *Cond; + const Node *Then; + const Node *Else; + +public: + ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_, + Prec Prec_) + : Node(KConditionalExpr, Prec_), Cond(Cond_), Then(Then_), Else(Else_) {} + + template void match(Fn F) const { + F(Cond, Then, Else, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Cond->printAsOperand(OB, getPrecedence()); + OB += " ? "; + Then->printAsOperand(OB); + OB += " : "; + Else->printAsOperand(OB, Prec::Assign, true); + } +}; + +class MemberExpr : public Node { + const Node *LHS; + const std::string_view Kind; + const Node *RHS; + +public: + MemberExpr(const Node *LHS_, std::string_view Kind_, const Node *RHS_, + Prec Prec_) + : Node(KMemberExpr, Prec_), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + + template void match(Fn F) const { + F(LHS, Kind, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + LHS->printAsOperand(OB, getPrecedence(), true); + OB += Kind; + RHS->printAsOperand(OB, getPrecedence(), false); + } +}; + +class SubobjectExpr : public Node { + const Node *Type; + const Node *SubExpr; + std::string_view Offset; + NodeArray UnionSelectors; + bool OnePastTheEnd; + +public: + SubobjectExpr(const Node *Type_, const Node *SubExpr_, + std::string_view Offset_, NodeArray UnionSelectors_, + bool OnePastTheEnd_) + : Node(KSubobjectExpr), Type(Type_), SubExpr(SubExpr_), Offset(Offset_), + UnionSelectors(UnionSelectors_), OnePastTheEnd(OnePastTheEnd_) {} + + template void match(Fn F) const { + F(Type, SubExpr, Offset, UnionSelectors, OnePastTheEnd); + } + + void printLeft(OutputBuffer &OB) const override { + SubExpr->print(OB); + OB += ".<"; + Type->print(OB); + OB += " at offset "; + if (Offset.empty()) { + OB += "0"; + } else if (Offset[0] == 'n') { + OB += "-"; + OB += std::string_view(Offset.data() + 1, Offset.size() - 1); + } else { + OB += Offset; + } + OB += ">"; + } +}; + +class EnclosingExpr : public Node { + const std::string_view Prefix; + const Node *Infix; + const std::string_view Postfix; + +public: + EnclosingExpr(std::string_view Prefix_, const Node *Infix_, + Prec Prec_ = Prec::Primary) + : Node(KEnclosingExpr, Prec_), Prefix(Prefix_), Infix(Infix_) {} + + template void match(Fn F) const { + F(Prefix, Infix, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + OB.printOpen(); + Infix->print(OB); + OB.printClose(); + OB += Postfix; + } +}; + +class CastExpr : public Node { + // cast_kind(from) + const std::string_view CastKind; + const Node *To; + const Node *From; + +public: + CastExpr(std::string_view CastKind_, const Node *To_, const Node *From_, + Prec Prec_) + : Node(KCastExpr, Prec_), CastKind(CastKind_), To(To_), From(From_) {} + + template void match(Fn F) const { + F(CastKind, To, From, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB += CastKind; + { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "<"; + To->printLeft(OB); + OB += ">"; + } + OB.printOpen(); + From->printAsOperand(OB); + OB.printClose(); + } +}; + +class SizeofParamPackExpr : public Node { + const Node *Pack; + +public: + SizeofParamPackExpr(const Node *Pack_) + : Node(KSizeofParamPackExpr), Pack(Pack_) {} + + template void match(Fn F) const { F(Pack); } + + void printLeft(OutputBuffer &OB) const override { + OB += "sizeof..."; + OB.printOpen(); + ParameterPackExpansion PPE(Pack); + PPE.printLeft(OB); + OB.printClose(); + } +}; + +class CallExpr : public Node { + const Node *Callee; + NodeArray Args; + +public: + CallExpr(const Node *Callee_, NodeArray Args_, Prec Prec_) + : Node(KCallExpr, Prec_), Callee(Callee_), Args(Args_) {} + + template void match(Fn F) const { + F(Callee, Args, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Callee->print(OB); + OB.printOpen(); + Args.printWithComma(OB); + OB.printClose(); + } +}; + +class NewExpr : public Node { + // new (expr_list) type(init_list) + NodeArray ExprList; + Node *Type; + NodeArray InitList; + bool IsGlobal; // ::operator new ? + bool IsArray; // new[] ? +public: + NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_, + bool IsArray_, Prec Prec_) + : Node(KNewExpr, Prec_), ExprList(ExprList_), Type(Type_), + InitList(InitList_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} + + template void match(Fn F) const { + F(ExprList, Type, InitList, IsGlobal, IsArray, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + if (IsGlobal) + OB += "::"; + OB += "new"; + if (IsArray) + OB += "[]"; + if (!ExprList.empty()) { + OB.printOpen(); + ExprList.printWithComma(OB); + OB.printClose(); + } + OB += " "; + Type->print(OB); + if (!InitList.empty()) { + OB.printOpen(); + InitList.printWithComma(OB); + OB.printClose(); + } + } +}; + +class DeleteExpr : public Node { + Node *Op; + bool IsGlobal; + bool IsArray; + +public: + DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_, Prec Prec_) + : Node(KDeleteExpr, Prec_), Op(Op_), IsGlobal(IsGlobal_), + IsArray(IsArray_) {} + + template void match(Fn F) const { + F(Op, IsGlobal, IsArray, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + if (IsGlobal) + OB += "::"; + OB += "delete"; + if (IsArray) + OB += "[]"; + OB += ' '; + Op->print(OB); + } +}; + +class PrefixExpr : public Node { + std::string_view Prefix; + Node *Child; + +public: + PrefixExpr(std::string_view Prefix_, Node *Child_, Prec Prec_) + : Node(KPrefixExpr, Prec_), Prefix(Prefix_), Child(Child_) {} + + template void match(Fn F) const { + F(Prefix, Child, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + Child->printAsOperand(OB, getPrecedence()); + } +}; + +class FunctionParam : public Node { + std::string_view Number; + +public: + FunctionParam(std::string_view Number_) + : Node(KFunctionParam), Number(Number_) {} + + template void match(Fn F) const { F(Number); } + + void printLeft(OutputBuffer &OB) const override { + OB += "fp"; + OB += Number; + } +}; + +class ConversionExpr : public Node { + const Node *Type; + NodeArray Expressions; + +public: + ConversionExpr(const Node *Type_, NodeArray Expressions_, Prec Prec_) + : Node(KConversionExpr, Prec_), Type(Type_), Expressions(Expressions_) {} + + template void match(Fn F) const { + F(Type, Expressions, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + Expressions.printWithComma(OB); + OB.printClose(); + } +}; + +class PointerToMemberConversionExpr : public Node { + const Node *Type; + const Node *SubExpr; + std::string_view Offset; + +public: + PointerToMemberConversionExpr(const Node *Type_, const Node *SubExpr_, + std::string_view Offset_, Prec Prec_) + : Node(KPointerToMemberConversionExpr, Prec_), Type(Type_), + SubExpr(SubExpr_), Offset(Offset_) {} + + template void match(Fn F) const { + F(Type, SubExpr, Offset, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + SubExpr->print(OB); + OB.printClose(); + } +}; + +class InitListExpr : public Node { + const Node *Ty; + NodeArray Inits; +public: + InitListExpr(const Node *Ty_, NodeArray Inits_) + : Node(KInitListExpr), Ty(Ty_), Inits(Inits_) {} + + template void match(Fn F) const { F(Ty, Inits); } + + void printLeft(OutputBuffer &OB) const override { + if (Ty) + Ty->print(OB); + OB += '{'; + Inits.printWithComma(OB); + OB += '}'; + } +}; + +class BracedExpr : public Node { + const Node *Elem; + const Node *Init; + bool IsArray; +public: + BracedExpr(const Node *Elem_, const Node *Init_, bool IsArray_) + : Node(KBracedExpr), Elem(Elem_), Init(Init_), IsArray(IsArray_) {} + + template void match(Fn F) const { F(Elem, Init, IsArray); } + + void printLeft(OutputBuffer &OB) const override { + if (IsArray) { + OB += '['; + Elem->print(OB); + OB += ']'; + } else { + OB += '.'; + Elem->print(OB); + } + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + OB += " = "; + Init->print(OB); + } +}; + +class BracedRangeExpr : public Node { + const Node *First; + const Node *Last; + const Node *Init; +public: + BracedRangeExpr(const Node *First_, const Node *Last_, const Node *Init_) + : Node(KBracedRangeExpr), First(First_), Last(Last_), Init(Init_) {} + + template void match(Fn F) const { F(First, Last, Init); } + + void printLeft(OutputBuffer &OB) const override { + OB += '['; + First->print(OB); + OB += " ... "; + Last->print(OB); + OB += ']'; + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + OB += " = "; + Init->print(OB); + } +}; + +class FoldExpr : public Node { + const Node *Pack, *Init; + std::string_view OperatorName; + bool IsLeftFold; + +public: + FoldExpr(bool IsLeftFold_, std::string_view OperatorName_, const Node *Pack_, + const Node *Init_) + : Node(KFoldExpr), Pack(Pack_), Init(Init_), OperatorName(OperatorName_), + IsLeftFold(IsLeftFold_) {} + + template void match(Fn F) const { + F(IsLeftFold, OperatorName, Pack, Init); + } + + void printLeft(OutputBuffer &OB) const override { + auto PrintPack = [&] { + OB.printOpen(); + ParameterPackExpansion(Pack).print(OB); + OB.printClose(); + }; + + OB.printOpen(); + // Either '[init op ]... op pack' or 'pack op ...[ op init]' + // Refactored to '[(init|pack) op ]...[ op (pack|init)]' + // Fold expr operands are cast-expressions + if (!IsLeftFold || Init != nullptr) { + // '(init|pack) op ' + if (IsLeftFold) + Init->printAsOperand(OB, Prec::Cast, true); + else + PrintPack(); + OB << " " << OperatorName << " "; + } + OB << "..."; + if (IsLeftFold || Init != nullptr) { + // ' op (init|pack)' + OB << " " << OperatorName << " "; + if (IsLeftFold) + PrintPack(); + else + Init->printAsOperand(OB, Prec::Cast, true); + } + OB.printClose(); + } +}; + +class ThrowExpr : public Node { + const Node *Op; + +public: + ThrowExpr(const Node *Op_) : Node(KThrowExpr), Op(Op_) {} + + template void match(Fn F) const { F(Op); } + + void printLeft(OutputBuffer &OB) const override { + OB += "throw "; + Op->print(OB); + } +}; + +class BoolExpr : public Node { + bool Value; + +public: + BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {} + + template void match(Fn F) const { F(Value); } + + void printLeft(OutputBuffer &OB) const override { + OB += Value ? std::string_view("true") : std::string_view("false"); + } +}; + +class StringLiteral : public Node { + const Node *Type; + +public: + StringLiteral(const Node *Type_) : Node(KStringLiteral), Type(Type_) {} + + template void match(Fn F) const { F(Type); } + + void printLeft(OutputBuffer &OB) const override { + OB += "\"<"; + Type->print(OB); + OB += ">\""; + } +}; + +class LambdaExpr : public Node { + const Node *Type; + +public: + LambdaExpr(const Node *Type_) : Node(KLambdaExpr), Type(Type_) {} + + template void match(Fn F) const { F(Type); } + + void printLeft(OutputBuffer &OB) const override { + OB += "[]"; + if (Type->getKind() == KClosureTypeName) + static_cast(Type)->printDeclarator(OB); + OB += "{...}"; + } +}; + +class EnumLiteral : public Node { + // ty(integer) + const Node *Ty; + std::string_view Integer; + +public: + EnumLiteral(const Node *Ty_, std::string_view Integer_) + : Node(KEnumLiteral), Ty(Ty_), Integer(Integer_) {} + + template void match(Fn F) const { F(Ty, Integer); } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Ty->print(OB); + OB.printClose(); + + if (Integer[0] == 'n') + OB << '-' << std::string_view(Integer.data() + 1, Integer.size() - 1); + else + OB << Integer; + } +}; + +class IntegerLiteral : public Node { + std::string_view Type; + std::string_view Value; + +public: + IntegerLiteral(std::string_view Type_, std::string_view Value_) + : Node(KIntegerLiteral), Type(Type_), Value(Value_) {} + + template void match(Fn F) const { F(Type, Value); } + + void printLeft(OutputBuffer &OB) const override { + if (Type.size() > 3) { + OB.printOpen(); + OB += Type; + OB.printClose(); + } + + if (Value[0] == 'n') + OB << '-' << std::string_view(Value.data() + 1, Value.size() - 1); + else + OB += Value; + + if (Type.size() <= 3) + OB += Type; + } +}; + +template struct FloatData; + +namespace float_literal_impl { +constexpr Node::Kind getFloatLiteralKind(float *) { + return Node::KFloatLiteral; +} +constexpr Node::Kind getFloatLiteralKind(double *) { + return Node::KDoubleLiteral; +} +constexpr Node::Kind getFloatLiteralKind(long double *) { + return Node::KLongDoubleLiteral; +} +} + +template class FloatLiteralImpl : public Node { + const std::string_view Contents; + + static constexpr Kind KindForClass = + float_literal_impl::getFloatLiteralKind((Float *)nullptr); + +public: + FloatLiteralImpl(std::string_view Contents_) + : Node(KindForClass), Contents(Contents_) {} + + template void match(Fn F) const { F(Contents); } + + void printLeft(OutputBuffer &OB) const override { + const size_t N = FloatData::mangled_size; + if (Contents.size() >= N) { + union { + Float value; + char buf[sizeof(Float)]; + }; + const char *t = Contents.data(); + const char *last = t + N; + char *e = buf; + for (; t != last; ++t, ++e) { + unsigned d1 = isdigit(*t) ? static_cast(*t - '0') + : static_cast(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast(*t - '0') + : static_cast(*t - 'a' + 10); + *e = static_cast((d1 << 4) + d0); + } +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[FloatData::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), FloatData::spec, value); + OB += std::string_view(num, n); + } + } +}; + +using FloatLiteral = FloatLiteralImpl; +using DoubleLiteral = FloatLiteralImpl; +using LongDoubleLiteral = FloatLiteralImpl; + +/// Visit the node. Calls \c F(P), where \c P is the node cast to the +/// appropriate derived class. +template +void Node::visit(Fn F) const { + switch (K) { +#define NODE(X) \ + case K##X: \ + return F(static_cast(this)); +#include "third_party/libcxxabi/demangle/ItaniumNodes.def" + } + assert(0 && "unknown mangling node kind"); +} + +/// Determine the kind of a node from its type. +template struct NodeKind; +#define NODE(X) \ + template <> struct NodeKind { \ + static constexpr Node::Kind Kind = Node::K##X; \ + static constexpr const char *name() { return #X; } \ + }; +#include "third_party/libcxxabi/demangle/ItaniumNodes.def" + +template struct AbstractManglingParser { + const char *First; + const char *Last; + + // Name stack, this is used by the parser to hold temporary names that were + // parsed. The parser collapses multiple names into new nodes to construct + // the AST. Once the parser is finished, names.size() == 1. + PODSmallVector Names; + + // Substitution table. Itanium supports name substitutions as a means of + // compression. The string "S42_" refers to the 44nd entry (base-36) in this + // table. + PODSmallVector Subs; + + using TemplateParamList = PODSmallVector; + + class ScopedTemplateParamList { + AbstractManglingParser *Parser; + size_t OldNumTemplateParamLists; + TemplateParamList Params; + + public: + ScopedTemplateParamList(AbstractManglingParser *TheParser) + : Parser(TheParser), + OldNumTemplateParamLists(TheParser->TemplateParams.size()) { + Parser->TemplateParams.push_back(&Params); + } + ~ScopedTemplateParamList() { + assert(Parser->TemplateParams.size() >= OldNumTemplateParamLists); + Parser->TemplateParams.dropBack(OldNumTemplateParamLists); + } + }; + + // Template parameter table. Like the above, but referenced like "T42_". + // This has a smaller size compared to Subs and Names because it can be + // stored on the stack. + TemplateParamList OuterTemplateParams; + + // Lists of template parameters indexed by template parameter depth, + // referenced like "TL2_4_". If nonempty, element 0 is always + // OuterTemplateParams; inner elements are always template parameter lists of + // lambda expressions. For a generic lambda with no explicit template + // parameter list, the corresponding parameter list pointer will be null. + PODSmallVector TemplateParams; + + // Set of unresolved forward references. These can occur in a + // conversion operator's type, and are resolved in the enclosing . + PODSmallVector ForwardTemplateRefs; + + bool TryToParseTemplateArgs = true; + bool PermitForwardTemplateReferences = false; + size_t ParsingLambdaParamsAtLevel = (size_t)-1; + + unsigned NumSyntheticTemplateParameters[3] = {}; + + Alloc ASTAllocator; + + AbstractManglingParser(const char *First_, const char *Last_) + : First(First_), Last(Last_) {} + + Derived &getDerived() { return static_cast(*this); } + + void reset(const char *First_, const char *Last_) { + First = First_; + Last = Last_; + Names.clear(); + Subs.clear(); + TemplateParams.clear(); + ParsingLambdaParamsAtLevel = (size_t)-1; + TryToParseTemplateArgs = true; + PermitForwardTemplateReferences = false; + for (int I = 0; I != 3; ++I) + NumSyntheticTemplateParameters[I] = 0; + ASTAllocator.reset(); + } + + template Node *make(Args &&... args) { + return ASTAllocator.template makeNode(std::forward(args)...); + } + + template NodeArray makeNodeArray(It begin, It end) { + size_t sz = static_cast(end - begin); + void *mem = ASTAllocator.allocateNodeArray(sz); + Node **data = new (mem) Node *[sz]; + std::copy(begin, end, data); + return NodeArray(data, sz); + } + + NodeArray popTrailingNodeArray(size_t FromPosition) { + assert(FromPosition <= Names.size()); + NodeArray res = + makeNodeArray(Names.begin() + (long)FromPosition, Names.end()); + Names.dropBack(FromPosition); + return res; + } + + bool consumeIf(std::string_view S) { + if (starts_with(std::string_view(First, Last - First), S)) { + First += S.size(); + return true; + } + return false; + } + + bool consumeIf(char C) { + if (First != Last && *First == C) { + ++First; + return true; + } + return false; + } + + char consume() { return First != Last ? *First++ : '\0'; } + + char look(unsigned Lookahead = 0) const { + if (static_cast(Last - First) <= Lookahead) + return '\0'; + return First[Lookahead]; + } + + size_t numLeft() const { return static_cast(Last - First); } + + std::string_view parseNumber(bool AllowNegative = false); + Qualifiers parseCVQualifiers(); + bool parsePositiveInteger(size_t *Out); + std::string_view parseBareSourceName(); + + bool parseSeqId(size_t *Out); + Node *parseSubstitution(); + Node *parseTemplateParam(); + Node *parseTemplateParamDecl(); + Node *parseTemplateArgs(bool TagTemplates = false); + Node *parseTemplateArg(); + + /// Parse the production. + Node *parseExpr(); + Node *parsePrefixExpr(std::string_view Kind, Node::Prec Prec); + Node *parseBinaryExpr(std::string_view Kind, Node::Prec Prec); + Node *parseIntegerLiteral(std::string_view Lit); + Node *parseExprPrimary(); + template Node *parseFloatingLiteral(); + Node *parseFunctionParam(); + Node *parseConversionExpr(); + Node *parseBracedExpr(); + Node *parseFoldExpr(); + Node *parsePointerToMemberConversionExpr(Node::Prec Prec); + Node *parseSubobjectExpr(); + + /// Parse the production. + Node *parseType(); + Node *parseFunctionType(); + Node *parseVectorType(); + Node *parseDecltype(); + Node *parseArrayType(); + Node *parsePointerToMemberType(); + Node *parseClassEnumType(); + Node *parseQualifiedType(); + + Node *parseEncoding(); + bool parseCallOffset(); + Node *parseSpecialName(); + + /// Holds some extra information about a that is being parsed. This + /// information is only pertinent if the refers to an . + struct NameState { + bool CtorDtorConversion = false; + bool EndsWithTemplateArgs = false; + Qualifiers CVQualifiers = QualNone; + FunctionRefQual ReferenceQualifier = FrefQualNone; + size_t ForwardTemplateRefsBegin; + + NameState(AbstractManglingParser *Enclosing) + : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {} + }; + + bool resolveForwardTemplateRefs(NameState &State) { + size_t I = State.ForwardTemplateRefsBegin; + size_t E = ForwardTemplateRefs.size(); + for (; I < E; ++I) { + size_t Idx = ForwardTemplateRefs[I]->Index; + if (TemplateParams.empty() || !TemplateParams[0] || + Idx >= TemplateParams[0]->size()) + return true; + ForwardTemplateRefs[I]->Ref = (*TemplateParams[0])[Idx]; + } + ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin); + return false; + } + + /// Parse the production> + Node *parseName(NameState *State = nullptr); + Node *parseLocalName(NameState *State); + Node *parseOperatorName(NameState *State); + bool parseModuleNameOpt(ModuleName *&Module); + Node *parseUnqualifiedName(NameState *State, Node *Scope, ModuleName *Module); + Node *parseUnnamedTypeName(NameState *State); + Node *parseSourceName(NameState *State); + Node *parseUnscopedName(NameState *State, bool *isSubstName); + Node *parseNestedName(NameState *State); + Node *parseCtorDtorName(Node *&SoFar, NameState *State); + + Node *parseAbiTags(Node *N); + + struct OperatorInfo { + enum OIKind : unsigned char { + Prefix, // Prefix unary: @ expr + Postfix, // Postfix unary: expr @ + Binary, // Binary: lhs @ rhs + Array, // Array index: lhs [ rhs ] + Member, // Member access: lhs @ rhs + New, // New + Del, // Delete + Call, // Function call: expr (expr*) + CCast, // C cast: (type)expr + Conditional, // Conditional: expr ? expr : expr + NameOnly, // Overload only, not allowed in expression. + // Below do not have operator names + NamedCast, // Named cast, @(expr) + OfIdOp, // alignof, sizeof, typeid + + Unnameable = NamedCast, + }; + char Enc[2]; // Encoding + OIKind Kind; // Kind of operator + bool Flag : 1; // Entry-specific flag + Node::Prec Prec : 7; // Precedence + const char *Name; // Spelling + + public: + constexpr OperatorInfo(const char (&E)[3], OIKind K, bool F, Node::Prec P, + const char *N) + : Enc{E[0], E[1]}, Kind{K}, Flag{F}, Prec{P}, Name{N} {} + + public: + bool operator<(const OperatorInfo &Other) const { + return *this < Other.Enc; + } + bool operator<(const char *Peek) const { + return Enc[0] < Peek[0] || (Enc[0] == Peek[0] && Enc[1] < Peek[1]); + } + bool operator==(const char *Peek) const { + return Enc[0] == Peek[0] && Enc[1] == Peek[1]; + } + bool operator!=(const char *Peek) const { return !this->operator==(Peek); } + + public: + std::string_view getSymbol() const { + std::string_view Res = Name; + if (Kind < Unnameable) { + assert(starts_with(Res, "operator") && + "operator name does not start with 'operator'"); + Res.remove_prefix(sizeof("operator") - 1); + if (starts_with(Res, ' ')) + Res.remove_prefix(1); + } + return Res; + } + std::string_view getName() const { return Name; } + OIKind getKind() const { return Kind; } + bool getFlag() const { return Flag; } + Node::Prec getPrecedence() const { return Prec; } + }; + static const OperatorInfo Ops[]; + static const size_t NumOps; + const OperatorInfo *parseOperatorEncoding(); + + /// Parse the production. + Node *parseUnresolvedName(bool Global); + Node *parseSimpleId(); + Node *parseBaseUnresolvedName(); + Node *parseUnresolvedType(); + Node *parseDestructorName(); + + /// Top-level entry point into the parser. + Node *parse(); +}; + +const char* parse_discriminator(const char* first, const char* last); + +// ::= // N +// ::= # See Scope Encoding below // Z +// ::= +// ::= +// +// ::= +// ::= +template +Node *AbstractManglingParser::parseName(NameState *State) { + if (look() == 'N') + return getDerived().parseNestedName(State); + if (look() == 'Z') + return getDerived().parseLocalName(State); + + Node *Result = nullptr; + bool IsSubst = false; + + Result = getDerived().parseUnscopedName(State, &IsSubst); + if (!Result) + return nullptr; + + if (look() == 'I') { + // ::= + if (!IsSubst) + // An unscoped-template-name is substitutable. + Subs.push_back(Result); + Node *TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) + State->EndsWithTemplateArgs = true; + Result = make(Result, TA); + } else if (IsSubst) { + // The substitution case must be followed by . + return nullptr; + } + + return Result; +} + +// := Z E [] +// := Z E s [] +// := Z Ed [ ] _ +template +Node *AbstractManglingParser::parseLocalName(NameState *State) { + if (!consumeIf('Z')) + return nullptr; + Node *Encoding = getDerived().parseEncoding(); + if (Encoding == nullptr || !consumeIf('E')) + return nullptr; + + if (consumeIf('s')) { + First = parse_discriminator(First, Last); + auto *StringLitName = make("string literal"); + if (!StringLitName) + return nullptr; + return make(Encoding, StringLitName); + } + + if (consumeIf('d')) { + parseNumber(true); + if (!consumeIf('_')) + return nullptr; + Node *N = getDerived().parseName(State); + if (N == nullptr) + return nullptr; + return make(Encoding, N); + } + + Node *Entity = getDerived().parseName(State); + if (Entity == nullptr) + return nullptr; + First = parse_discriminator(First, Last); + return make(Encoding, Entity); +} + +// ::= +// ::= St # ::std:: +// [*] extension +template +Node * +AbstractManglingParser::parseUnscopedName(NameState *State, + bool *IsSubst) { + + Node *Std = nullptr; + if (consumeIf("St")) { + Std = make("std"); + if (Std == nullptr) + return nullptr; + } + + Node *Res = nullptr; + ModuleName *Module = nullptr; + if (look() == 'S') { + Node *S = getDerived().parseSubstitution(); + if (!S) + return nullptr; + if (S->getKind() == Node::KModuleName) + Module = static_cast(S); + else if (IsSubst && Std == nullptr) { + Res = S; + *IsSubst = true; + } else { + return nullptr; + } + } + + if (Res == nullptr || Std != nullptr) { + Res = getDerived().parseUnqualifiedName(State, Std, Module); + } + + return Res; +} + +// ::= [] L? [] +// ::= [] [] +// ::= [] L? [] +// ::= [] L? [] +// # structured binding declaration +// ::= [] L? DC + E +template +Node *AbstractManglingParser::parseUnqualifiedName( + NameState *State, Node *Scope, ModuleName *Module) { + if (getDerived().parseModuleNameOpt(Module)) + return nullptr; + + consumeIf('L'); + + Node *Result; + if (look() >= '1' && look() <= '9') { + Result = getDerived().parseSourceName(State); + } else if (look() == 'U') { + Result = getDerived().parseUnnamedTypeName(State); + } else if (consumeIf("DC")) { + // Structured binding + size_t BindingsBegin = Names.size(); + do { + Node *Binding = getDerived().parseSourceName(State); + if (Binding == nullptr) + return nullptr; + Names.push_back(Binding); + } while (!consumeIf('E')); + Result = make(popTrailingNodeArray(BindingsBegin)); + } else if (look() == 'C' || look() == 'D') { + // A . + if (Scope == nullptr || Module != nullptr) + return nullptr; + Result = getDerived().parseCtorDtorName(Scope, State); + } else { + Result = getDerived().parseOperatorName(State); + } + + if (Result != nullptr && Module != nullptr) + Result = make(Module, Result); + if (Result != nullptr) + Result = getDerived().parseAbiTags(Result); + if (Result != nullptr && Scope != nullptr) + Result = make(Scope, Result); + + return Result; +} + +// ::= +// ::= +// ::= # passed in by caller +// ::= W +// ::= W P +template +bool AbstractManglingParser::parseModuleNameOpt( + ModuleName *&Module) { + while (consumeIf('W')) { + bool IsPartition = consumeIf('P'); + Node *Sub = getDerived().parseSourceName(nullptr); + if (!Sub) + return true; + Module = + static_cast(make(Module, Sub, IsPartition)); + Subs.push_back(Module); + } + + return false; +} + +// ::= Ut [] _ +// ::= +// +// ::= Ul E [ ] _ +// +// ::= + # Parameter types or "v" if the lambda has no parameters +template +Node * +AbstractManglingParser::parseUnnamedTypeName(NameState *State) { + // refer to the innermost . Clear out any + // outer args that we may have inserted into TemplateParams. + if (State != nullptr) + TemplateParams.clear(); + + if (consumeIf("Ut")) { + std::string_view Count = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make(Count); + } + if (consumeIf("Ul")) { + ScopedOverride SwapParams(ParsingLambdaParamsAtLevel, + TemplateParams.size()); + ScopedTemplateParamList LambdaTemplateParams(this); + + size_t ParamsBegin = Names.size(); + while (look() == 'T' && + std::string_view("yptn").find(look(1)) != std::string_view::npos) { + Node *T = parseTemplateParamDecl(); + if (!T) + return nullptr; + Names.push_back(T); + } + NodeArray TempParams = popTrailingNodeArray(ParamsBegin); + + // FIXME: If TempParams is empty and none of the function parameters + // includes 'auto', we should remove LambdaTemplateParams from the + // TemplateParams list. Unfortunately, we don't find out whether there are + // any 'auto' parameters until too late in an example such as: + // + // template void f( + // decltype([](decltype([](T v) {}), + // auto) {})) {} + // template void f( + // decltype([](decltype([](T w) {}), + // int) {})) {} + // + // Here, the type of v is at level 2 but the type of w is at level 1. We + // don't find this out until we encounter the type of the next parameter. + // + // However, compilers can't actually cope with the former example in + // practice, and it's likely to be made ill-formed in future, so we don't + // need to support it here. + // + // If we encounter an 'auto' in the function parameter types, we will + // recreate a template parameter scope for it, but any intervening lambdas + // will be parsed in the 'wrong' template parameter depth. + if (TempParams.empty()) + TemplateParams.pop_back(); + + if (!consumeIf("vE")) { + do { + Node *P = getDerived().parseType(); + if (P == nullptr) + return nullptr; + Names.push_back(P); + } while (!consumeIf('E')); + } + NodeArray Params = popTrailingNodeArray(ParamsBegin); + + std::string_view Count = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make(TempParams, Params, Count); + } + if (consumeIf("Ub")) { + (void)parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make("'block-literal'"); + } + return nullptr; +} + +// ::= +template +Node *AbstractManglingParser::parseSourceName(NameState *) { + size_t Length = 0; + if (parsePositiveInteger(&Length)) + return nullptr; + if (numLeft() < Length || Length == 0) + return nullptr; + std::string_view Name(First, Length); + First += Length; + if (starts_with(Name, "_GLOBAL__N")) + return make("(anonymous namespace)"); + return make(Name); +} + +// Operator encodings +template +const typename AbstractManglingParser< + Derived, Alloc>::OperatorInfo AbstractManglingParser::Ops[] = { + // Keep ordered by encoding + {"aN", OperatorInfo::Binary, false, Node::Prec::Assign, "operator&="}, + {"aS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator="}, + {"aa", OperatorInfo::Binary, false, Node::Prec::AndIf, "operator&&"}, + {"ad", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator&"}, + {"an", OperatorInfo::Binary, false, Node::Prec::And, "operator&"}, + {"at", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "alignof "}, + {"aw", OperatorInfo::NameOnly, false, Node::Prec::Primary, + "operator co_await"}, + {"az", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "alignof "}, + {"cc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "const_cast"}, + {"cl", OperatorInfo::Call, false, Node::Prec::Postfix, "operator()"}, + {"cm", OperatorInfo::Binary, false, Node::Prec::Comma, "operator,"}, + {"co", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator~"}, + {"cv", OperatorInfo::CCast, false, Node::Prec::Cast, "operator"}, // C Cast + {"dV", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/="}, + {"da", OperatorInfo::Del, /*Ary*/ true, Node::Prec::Unary, + "operator delete[]"}, + {"dc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "dynamic_cast"}, + {"de", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator*"}, + {"dl", OperatorInfo::Del, /*Ary*/ false, Node::Prec::Unary, + "operator delete"}, + {"ds", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, + "operator.*"}, + {"dt", OperatorInfo::Member, /*Named*/ false, Node::Prec::Postfix, + "operator."}, + {"dv", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/"}, + {"eO", OperatorInfo::Binary, false, Node::Prec::Assign, "operator^="}, + {"eo", OperatorInfo::Binary, false, Node::Prec::Xor, "operator^"}, + {"eq", OperatorInfo::Binary, false, Node::Prec::Equality, "operator=="}, + {"ge", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>="}, + {"gt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>"}, + {"ix", OperatorInfo::Array, false, Node::Prec::Postfix, "operator[]"}, + {"lS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator<<="}, + {"le", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<="}, + {"ls", OperatorInfo::Binary, false, Node::Prec::Shift, "operator<<"}, + {"lt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<"}, + {"mI", OperatorInfo::Binary, false, Node::Prec::Assign, "operator-="}, + {"mL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator*="}, + {"mi", OperatorInfo::Binary, false, Node::Prec::Additive, "operator-"}, + {"ml", OperatorInfo::Binary, false, Node::Prec::Multiplicative, + "operator*"}, + {"mm", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator--"}, + {"na", OperatorInfo::New, /*Ary*/ true, Node::Prec::Unary, + "operator new[]"}, + {"ne", OperatorInfo::Binary, false, Node::Prec::Equality, "operator!="}, + {"ng", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator-"}, + {"nt", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator!"}, + {"nw", OperatorInfo::New, /*Ary*/ false, Node::Prec::Unary, "operator new"}, + {"oR", OperatorInfo::Binary, false, Node::Prec::Assign, "operator|="}, + {"oo", OperatorInfo::Binary, false, Node::Prec::OrIf, "operator||"}, + {"or", OperatorInfo::Binary, false, Node::Prec::Ior, "operator|"}, + {"pL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator+="}, + {"pl", OperatorInfo::Binary, false, Node::Prec::Additive, "operator+"}, + {"pm", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, + "operator->*"}, + {"pp", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator++"}, + {"ps", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator+"}, + {"pt", OperatorInfo::Member, /*Named*/ true, Node::Prec::Postfix, + "operator->"}, + {"qu", OperatorInfo::Conditional, false, Node::Prec::Conditional, + "operator?"}, + {"rM", OperatorInfo::Binary, false, Node::Prec::Assign, "operator%="}, + {"rS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator>>="}, + {"rc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, + "reinterpret_cast"}, + {"rm", OperatorInfo::Binary, false, Node::Prec::Multiplicative, + "operator%"}, + {"rs", OperatorInfo::Binary, false, Node::Prec::Shift, "operator>>"}, + {"sc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "static_cast"}, + {"ss", OperatorInfo::Binary, false, Node::Prec::Spaceship, "operator<=>"}, + {"st", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "sizeof "}, + {"sz", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "sizeof "}, + {"te", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Postfix, + "typeid "}, + {"ti", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Postfix, "typeid "}, +}; +template +const size_t AbstractManglingParser::NumOps = sizeof(Ops) / + sizeof(Ops[0]); + +// If the next 2 chars are an operator encoding, consume them and return their +// OperatorInfo. Otherwise return nullptr. +template +const typename AbstractManglingParser::OperatorInfo * +AbstractManglingParser::parseOperatorEncoding() { + if (numLeft() < 2) + return nullptr; + + // We can't use lower_bound as that can link to symbols in the C++ library, + // and this must remain independant of that. + size_t lower = 0u, upper = NumOps - 1; // Inclusive bounds. + while (upper != lower) { + size_t middle = (upper + lower) / 2; + if (Ops[middle] < First) + lower = middle + 1; + else + upper = middle; + } + if (Ops[lower] != First) + return nullptr; + + First += 2; + return &Ops[lower]; +} + +// ::= See parseOperatorEncoding() +// ::= li # operator "" +// ::= v # vendor extended operator +template +Node * +AbstractManglingParser::parseOperatorName(NameState *State) { + if (const auto *Op = parseOperatorEncoding()) { + if (Op->getKind() == OperatorInfo::CCast) { + // ::= cv # (cast) + ScopedOverride SaveTemplate(TryToParseTemplateArgs, false); + // If we're parsing an encoding, State != nullptr and the conversion + // operators' could have a that refers to some + // s further ahead in the mangled name. + ScopedOverride SavePermit(PermitForwardTemplateReferences, + PermitForwardTemplateReferences || + State != nullptr); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + if (State) State->CtorDtorConversion = true; + return make(Ty); + } + + if (Op->getKind() >= OperatorInfo::Unnameable) + /* Not a nameable operator. */ + return nullptr; + if (Op->getKind() == OperatorInfo::Member && !Op->getFlag()) + /* Not a nameable MemberExpr */ + return nullptr; + + return make(Op->getName()); + } + + if (consumeIf("li")) { + // ::= li # operator "" + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make(SN); + } + + if (consumeIf('v')) { + // ::= v # vendor extended operator + if (look() >= '0' && look() <= '9') { + First++; + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make(SN); + } + return nullptr; + } + + return nullptr; +} + +// ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C4 # gcc old-style "[unified]" constructor +// extension ::= C5 # the COMDAT used for ctors +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D4 # gcc old-style "[unified]" destructor +// extension ::= D5 # the COMDAT used for dtors +template +Node * +AbstractManglingParser::parseCtorDtorName(Node *&SoFar, + NameState *State) { + if (SoFar->getKind() == Node::KSpecialSubstitution) { + // Expand the special substitution. + SoFar = make( + static_cast(SoFar)); + if (!SoFar) + return nullptr; + } + + if (consumeIf('C')) { + bool IsInherited = consumeIf('I'); + if (look() != '1' && look() != '2' && look() != '3' && look() != '4' && + look() != '5') + return nullptr; + int Variant = look() - '0'; + ++First; + if (State) State->CtorDtorConversion = true; + if (IsInherited) { + if (getDerived().parseName(State) == nullptr) + return nullptr; + } + return make(SoFar, /*IsDtor=*/false, Variant); + } + + if (look() == 'D' && (look(1) == '0' || look(1) == '1' || look(1) == '2' || + look(1) == '4' || look(1) == '5')) { + int Variant = look(1) - '0'; + First += 2; + if (State) State->CtorDtorConversion = true; + return make(SoFar, /*IsDtor=*/true, Variant); + } + + return nullptr; +} + +// ::= N [] [] +// E +// ::= N [] [] +// E +// +// ::= +// ::= +// ::= +// ::= +// ::= # empty +// ::= +// ::= +// [*] extension +// +// := [] M +// +// ::=