diff --git a/lib/libcxxabi/include/__cxxabi_config.h b/lib/libcxxabi/include/__cxxabi_config.h index 7bc39ada8ddaf6b9020ae221648d4aa1adbae441..c97dd656e165cecb52423959c1c4fac90502d74a 100644 --- a/lib/libcxxabi/include/__cxxabi_config.h +++ b/lib/libcxxabi/include/__cxxabi_config.h @@ -10,7 +10,7 @@ #define ____CXXABI_CONFIG_H #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ - !defined(__ARM_DWARF_EH__) + !defined(__ARM_DWARF_EH__) && !defined(__SEH__) #define _LIBCXXABI_ARM_EHABI #endif @@ -97,4 +97,10 @@ # define _LIBCXXABI_NO_EXCEPTIONS #endif +#if defined(_WIN32) +#define _LIBCXXABI_DTOR_FUNC __thiscall +#else +#define _LIBCXXABI_DTOR_FUNC +#endif + #endif // ____CXXABI_CONFIG_H diff --git a/lib/libcxxabi/include/cxxabi.h b/lib/libcxxabi/include/cxxabi.h index eaa324dc6867e850c18f854c7316dfb8917e1d93..85cb4b36b8119be6e8cef58364ce1b2fd0a445b6 100644 --- a/lib/libcxxabi/include/cxxabi.h +++ b/lib/libcxxabi/include/cxxabi.h @@ -19,7 +19,7 @@ #include <__cxxabi_config.h> -#define _LIBCPPABI_VERSION 1002 +#define _LIBCPPABI_VERSION 15000 #define _LIBCXXABI_NORETURN __attribute__((noreturn)) #define _LIBCXXABI_ALWAYS_COLD __attribute__((cold)) @@ -47,7 +47,7 @@ __cxa_free_exception(void *thrown_exception) throw(); // 2.4.3 Throwing the Exception Object extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_throw(void *thrown_exception, std::type_info *tinfo, - void (*dest)(void *)); + void (_LIBCXXABI_DTOR_FUNC *dest)(void *)); // 2.5.3 Exception Handlers extern _LIBCXXABI_FUNC_VIS void * diff --git a/lib/libcxxabi/src/aix_state_tab_eh.inc b/lib/libcxxabi/src/aix_state_tab_eh.inc new file mode 100644 index 0000000000000000000000000000000000000000..38761310452429515b35e38a31371710826a1dd5 --- /dev/null +++ b/lib/libcxxabi/src/aix_state_tab_eh.inc @@ -0,0 +1,684 @@ +//===----------------------------------------------------------------------===// +// +// 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 uint32_t REG_EXCP_OBJ = 14; // Register to pass the address of the exception + // object from the personality to xlclang++ + // compiled code. + +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) { + // Jump to the handler. + _Unwind_SetGR(context, REG_EXCP_OBJ, reinterpret_cast(unwind_exception)); + _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); +} + +// __xlc_exception_handle +// This function is for xlclang++. It returns the address of the exception +// object set in gpr14 by the personality routine for xlclang++ compiled code. +_LIBCXXABI_FUNC_VIS uintptr_t __xlc_exception_handle() { + uintptr_t exceptionObject; + asm("mr %0, 14" : "=r"(exceptionObject)); + 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/lib/libcxxabi/src/cxa_default_handlers.cpp b/lib/libcxxabi/src/cxa_default_handlers.cpp index e0ccbe119585ea0f217335b0d32810639a761c0d..7bcfca069fb7f71be94159762a425a067fde02c5 100644 --- a/lib/libcxxabi/src/cxa_default_handlers.cpp +++ b/lib/libcxxabi/src/cxa_default_handlers.cpp @@ -10,6 +10,7 @@ //===----------------------------------------------------------------------===// #include +#include #include #include "abort_message.h" #include "cxxabi.h" @@ -20,67 +21,69 @@ #if !defined(LIBCXXABI_SILENT_TERMINATE) -_LIBCPP_SAFE_STATIC -static const char* cause = "uncaught"; +static constinit const char* cause = "uncaught"; -__attribute__((noreturn)) -static void demangling_terminate_handler() -{ #ifndef _LIBCXXABI_NO_EXCEPTIONS - // If there might be an uncaught exception +// 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 (globals) + + // 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)) { - __cxa_exception* exception_header = globals->caughtExceptions; - // If there is an uncaught exception - if (exception_header) - { - _Unwind_Exception* unwind_exception = - reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; - if (__isOurExceptionClass(unwind_exception)) - { - 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); -#if !defined(LIBCXXABI_NON_DEMANGLING_TERMINATE) - // Try to get demangled name of thrown_type - int status; - char buf[1024]; - size_t len = sizeof(buf); - const char* name = __cxa_demangle(thrown_type->name(), buf, &len, &status); - if (status != 0) - name = thrown_type->name(); -#else - const char* name = thrown_type->name(); -#endif - // 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 with %s exception of type %s: %s", - cause, name, e->what()); - } - else - // Else just note that we're terminating with an exception - abort_message("terminating with %s exception of type %s", - cause, name); - } - else - // Else we're terminating with a foreign exception - abort_message("terminating with %s foreign exception", cause); - } + // 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()); } -#endif - // Else just note that we're terminating + 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() @@ -91,22 +94,22 @@ static void demangling_unexpected_handler() static constexpr std::terminate_handler default_terminate_handler = demangling_terminate_handler; static constexpr std::terminate_handler default_unexpected_handler = demangling_unexpected_handler; -#else +#else // !LIBCXXABI_SILENT_TERMINATE static constexpr std::terminate_handler default_terminate_handler = ::abort; static constexpr std::terminate_handler default_unexpected_handler = std::terminate; -#endif +#endif // !LIBCXXABI_SILENT_TERMINATE // // Global variables that hold the pointers to the current handler // _LIBCXXABI_DATA_VIS -_LIBCPP_SAFE_STATIC std::terminate_handler __cxa_terminate_handler = default_terminate_handler; +constinit std::terminate_handler __cxa_terminate_handler = default_terminate_handler; _LIBCXXABI_DATA_VIS -_LIBCPP_SAFE_STATIC std::unexpected_handler __cxa_unexpected_handler = default_unexpected_handler; +constinit std::unexpected_handler __cxa_unexpected_handler = default_unexpected_handler; _LIBCXXABI_DATA_VIS -_LIBCPP_SAFE_STATIC std::new_handler __cxa_new_handler = 0; +constinit std::new_handler __cxa_new_handler = nullptr; namespace std { diff --git a/lib/libcxxabi/src/cxa_demangle.cpp b/lib/libcxxabi/src/cxa_demangle.cpp index bee4cfd5a119cea160d3e15f834b8e5d33ca16cb..ddab6d33358a5768973d36e3b3b0c1b6ed9a7436 100644 --- a/lib/libcxxabi/src/cxa_demangle.cpp +++ b/lib/libcxxabi/src/cxa_demangle.cpp @@ -173,6 +173,50 @@ struct DumpVisitor { 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"); diff --git a/lib/libcxxabi/src/cxa_exception.cpp b/lib/libcxxabi/src/cxa_exception.cpp index 36388d50dad207fa09f4712fc053fbaec7499d49..9cb2bf888811f1cb8ca869564bf55059252ffed3 100644 --- a/lib/libcxxabi/src/cxa_exception.cpp +++ b/lib/libcxxabi/src/cxa_exception.cpp @@ -254,7 +254,7 @@ will call terminate, assuming that there was no handler for the exception. */ void -__cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(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); @@ -341,10 +341,11 @@ 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(). In addition, we are saving lr in order to -align the stack to 16 bytes and lr will be used to identify the caller and its -frame information. _Unwind_Resume never return and we need to keep the original -lr so just branch to it. +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() @@ -381,15 +382,19 @@ asm(" .pushsection .text.__cxa_end_cleanup,\"ax\",%progbits\n" #if defined(__ARM_FEATURE_BTI_DEFAULT) " bti\n" #endif - " push {r1, r2, r3, lr}\n" + " push {r1, r2, r3, r4}\n" + " mov r4, lr\n" " bl __cxa_end_cleanup_impl\n" - " pop {r1, r2, r3, r4}\n" " mov lr, r4\n" #if defined(LIBCXXABI_BAREMETAL) " ldr r4, =_Unwind_Resume\n" - " bx r4\n" + " mov ip, r4\n" +#endif + " pop {r1, r2, r3, r4}\n" +#if defined(LIBCXXABI_BAREMETAL) + " bx ip\n" #else - " b _Unwind_Resume\n" + " b _Unwind_Resume\n" #endif " .popsection"); #endif // defined(_LIBCXXABI_ARM_EHABI) @@ -439,6 +444,14 @@ __cxa_begin_catch(void* unwind_arg) throw() ( 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 diff --git a/lib/libcxxabi/src/cxa_exception.h b/lib/libcxxabi/src/cxa_exception.h index 7a32fb653b07658ac22964ded398950c85bc0b1a..49dde28b258f7c37a5efbb30a6dd854d80e191dc 100644 --- a/lib/libcxxabi/src/cxa_exception.h +++ b/lib/libcxxabi/src/cxa_exception.h @@ -34,7 +34,7 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { // in the beginning of the struct, rather than before unwindHeader. void *reserve; - // This is a new field to support C++ 0x exception_ptr. + // 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. @@ -43,7 +43,7 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { // Manage the exception object itself. std::type_info *exceptionType; - void (*exceptionDestructor)(void *); + void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); std::unexpected_handler unexpectedHandler; std::terminate_handler terminateHandler; @@ -63,9 +63,9 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { #endif #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) - // This is a new field to support C++ 0x exception_ptr. + // This is a new field to support C++11 exception_ptr. // For binary compatibility it is placed where the compiler - // previously adding padded to 64-bit align unwindHeader. + // previously added padding to 64-bit align unwindHeader. size_t referenceCount; #endif _Unwind_Exception unwindHeader; @@ -81,7 +81,7 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { #endif std::type_info *exceptionType; - void (*exceptionDestructor)(void *); + void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); std::unexpected_handler unexpectedHandler; std::terminate_handler terminateHandler; diff --git a/lib/libcxxabi/src/cxa_guard_impl.h b/lib/libcxxabi/src/cxa_guard_impl.h index 5a7cbfd5cdb9d107e9373e20bb6d7c0d34be9b39..72940cc7e869ac7678d42fd431e50f5b8c9cdb42 100644 --- a/lib/libcxxabi/src/cxa_guard_impl.h +++ b/lib/libcxxabi/src/cxa_guard_impl.h @@ -619,7 +619,7 @@ struct GlobalStatic { static T instance; }; template -_LIBCPP_SAFE_STATIC T GlobalStatic::instance = {}; +_LIBCPP_CONSTINIT T GlobalStatic::instance = {}; enum class Implementation { NoThreads, GlobalMutex, Futex }; diff --git a/lib/libcxxabi/src/cxa_personality.cpp b/lib/libcxxabi/src/cxa_personality.cpp index f6e135f137c098eeee873c960c3d140ddb258d35..b28c58df6a2f90d0157afa00dbc316df33c49699 100644 --- a/lib/libcxxabi/src/cxa_personality.cpp +++ b/lib/libcxxabi/src/cxa_personality.cpp @@ -22,6 +22,15 @@ #include "private_typeinfo.h" #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 @@ -613,7 +622,7 @@ static void scan_eh_tab(scan_results &results, _Unwind_Action actions, results.reason = _URC_FATAL_PHASE1_ERROR; return; } - // Start scan by getting exception table address + // Start scan by getting exception table address. const uint8_t *lsda = (const uint8_t *)_Unwind_GetLanguageSpecificData(context); if (lsda == 0) { @@ -903,6 +912,8 @@ static _Unwind_Reason_Code __gxx_personality_imp _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 @@ -1015,7 +1026,7 @@ static _Unwind_Reason_Code continue_unwind(_Unwind_Exception* unwind_exception, } // ARM register names -#if !defined(LIBCXXABI_USE_LLVM_UNWINDER) +#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; @@ -1050,7 +1061,7 @@ __gxx_personality_v0(_Unwind_State state, bool native_exception = __isOurExceptionClass(unwind_exception); -#if !defined(LIBCXXABI_USE_LLVM_UNWINDER) +#if !defined(_LIBUNWIND_VERSION) // Copy the address of _Unwind_Control_Block to r12 so that // _Unwind_GetLanguageSpecificData() and _Unwind_GetRegionStart() can // return correct address. @@ -1112,7 +1123,7 @@ __gxx_personality_v0(_Unwind_State state, } // Either we didn't do a phase 1 search (due to forced unwinding), or - // phase 1 reported no catching-handlers. + // phase 1 reported no catching-handlers. // Search for a (non-catching) cleanup if (is_force_unwinding) scan_eh_tab( @@ -1296,3 +1307,9 @@ _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code __xlcxx_personality_v1( } // 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/lib/libcxxabi/src/demangle/ItaniumDemangle.h b/lib/libcxxabi/src/demangle/ItaniumDemangle.h index db65c60e7e5e6cd1a07f82415bf65df3bc5219dc..e3f0c6dfecdc29d03de7ac04a5798d9554fcf8f0 100644 --- a/lib/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/lib/libcxxabi/src/demangle/ItaniumDemangle.h @@ -16,10 +16,6 @@ #ifndef DEMANGLE_ITANIUMDEMANGLE_H #define DEMANGLE_ITANIUMDEMANGLE_H -// FIXME: (possibly) incomplete list of features that clang mangles that this -// file does not yet support: -// - C++ modules TS - #include "DemangleConfig.h" #include "StringView.h" #include "Utility.h" @@ -32,85 +28,6 @@ #include #include -#define FOR_EACH_NODE_KIND(X) \ - X(NodeArrayNode) \ - X(DotSuffix) \ - X(VendorExtQualType) \ - X(QualType) \ - X(ConversionOperatorType) \ - X(PostfixQualifiedType) \ - X(ElaboratedTypeSpefType) \ - X(NameType) \ - X(AbiTagAttr) \ - X(EnableIfAttr) \ - X(ObjCProtoName) \ - X(PointerType) \ - X(ReferenceType) \ - X(PointerToMemberType) \ - X(ArrayType) \ - X(FunctionType) \ - X(NoexceptSpec) \ - X(DynamicExceptionSpec) \ - X(FunctionEncoding) \ - X(LiteralOperator) \ - X(SpecialName) \ - X(CtorVtableSpecialName) \ - X(QualifiedName) \ - X(NestedName) \ - X(LocalName) \ - X(VectorType) \ - X(PixelVectorType) \ - X(BinaryFPType) \ - X(SyntheticTemplateParamName) \ - X(TypeTemplateParamDecl) \ - X(NonTypeTemplateParamDecl) \ - X(TemplateTemplateParamDecl) \ - X(TemplateParamPackDecl) \ - X(ParameterPack) \ - X(TemplateArgumentPack) \ - X(ParameterPackExpansion) \ - X(TemplateArgs) \ - X(ForwardTemplateReference) \ - X(NameWithTemplateArgs) \ - X(GlobalQualifiedName) \ - X(StdQualifiedName) \ - X(ExpandedSpecialSubstitution) \ - X(SpecialSubstitution) \ - X(CtorDtorName) \ - X(DtorName) \ - X(UnnamedTypeName) \ - X(ClosureTypeName) \ - X(StructuredBindingName) \ - X(BinaryExpr) \ - X(ArraySubscriptExpr) \ - X(PostfixExpr) \ - X(ConditionalExpr) \ - X(MemberExpr) \ - X(SubobjectExpr) \ - X(EnclosingExpr) \ - X(CastExpr) \ - X(SizeofParamPackExpr) \ - X(CallExpr) \ - X(NewExpr) \ - X(DeleteExpr) \ - X(PrefixExpr) \ - X(FunctionParam) \ - X(ConversionExpr) \ - X(PointerToMemberConversionExpr) \ - X(InitListExpr) \ - X(FoldExpr) \ - X(ThrowExpr) \ - X(BoolExpr) \ - X(StringLiteral) \ - X(LambdaExpr) \ - X(EnumLiteral) \ - X(IntegerLiteral) \ - X(FloatLiteral) \ - X(DoubleLiteral) \ - X(LongDoubleLiteral) \ - X(BracedExpr) \ - X(BracedRangeExpr) - DEMANGLE_NAMESPACE_BEGIN template class PODSmallVector { @@ -238,37 +155,68 @@ public: class Node { public: enum Kind : unsigned char { -#define ENUMERATOR(NodeKind) K ## NodeKind, - FOR_EACH_NODE_KIND(ENUMERATOR) -#undef ENUMERATOR +#define NODE(NodeKind) K##NodeKind, +#include "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; + Cache RHSComponentCache : 2; /// Track if this node is a (possibly qualified) array type. This can affect /// how we format the output string. - Cache ArrayCache; + Cache ArrayCache : 2; /// Track if this node is a (possibly qualified) function type. This can /// affect how we format the output string. - Cache FunctionCache; + Cache FunctionCache : 2; public: - Node(Kind K_, Cache RHSComponentCache_ = Cache::No, - Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No) - : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_), - FunctionCache(FunctionCache_) {} + 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; @@ -299,6 +247,8 @@ public: 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; } @@ -307,6 +257,19 @@ public: // 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) @@ -356,7 +319,7 @@ public: if (!FirstElement) OB += ", "; size_t AfterComma = OB.getCurrentPosition(); - Elements[Idx]->print(OB); + Elements[Idx]->printAsOperand(OB, Node::Prec::Comma); // Elements[Idx] is an empty parameter pack expansion, we should erase the // comma we just printed. @@ -494,7 +457,7 @@ class PostfixQualifiedType final : public Node { const StringView Postfix; public: - PostfixQualifiedType(Node *Ty_, StringView Postfix_) + PostfixQualifiedType(const Node *Ty_, StringView Postfix_) : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} template void match(Fn F) const { F(Ty, Postfix); } @@ -519,6 +482,26 @@ public: 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 { StringView Kind; Node *Child; @@ -693,7 +676,7 @@ public: void printLeft(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); std::pair Collapsed = collapse(OB); if (!Collapsed.second) return; @@ -708,7 +691,7 @@ public: void printRight(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); std::pair Collapsed = collapse(OB); if (!Collapsed.second) return; @@ -815,9 +798,9 @@ public: } void printRight(OutputBuffer &OB) const override { - OB += "("; + OB.printOpen(); Params.printWithComma(OB); - OB += ")"; + OB.printClose(); Ret->printRight(OB); if (CVQuals & QualConst) @@ -847,9 +830,10 @@ public: template void match(Fn F) const { F(E); } void printLeft(OutputBuffer &OB) const override { - OB += "noexcept("; - E->print(OB); - OB += ")"; + OB += "noexcept"; + OB.printOpen(); + E->printAsOperand(OB); + OB.printClose(); } }; @@ -862,9 +846,10 @@ public: template void match(Fn F) const { F(Types); } void printLeft(OutputBuffer &OB) const override { - OB += "throw("; + OB += "throw"; + OB.printOpen(); Types.printWithComma(OB); - OB += ')'; + OB.printClose(); } }; @@ -910,9 +895,9 @@ public: } void printRight(OutputBuffer &OB) const override { - OB += "("; + OB.printOpen(); Params.printWithComma(OB); - OB += ")"; + OB.printClose(); if (Ret) Ret->printRight(OB); @@ -1001,6 +986,46 @@ struct NestedName : Node { } }; +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); } + + StringView 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; @@ -1042,9 +1067,8 @@ class VectorType final : public Node { const Node *Dimension; public: - VectorType(const Node *BaseType_, Node *Dimension_) - : Node(KVectorType), BaseType(BaseType_), - Dimension(Dimension_) {} + VectorType(const Node *BaseType_, const Node *Dimension_) + : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_) {} template void match(Fn F) const { F(BaseType, Dimension); } @@ -1176,6 +1200,7 @@ public: 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 "; @@ -1311,8 +1336,8 @@ public: void printLeft(OutputBuffer &OB) const override { constexpr unsigned Max = std::numeric_limits::max(); - SwapAndRestore SavePackIdx(OB.CurrentPackIndex, Max); - SwapAndRestore SavePackMax(OB.CurrentPackMax, 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, @@ -1353,10 +1378,9 @@ public: NodeArray getParams() { return Params; } void printLeft(OutputBuffer &OB) const override { + ScopedOverride LT(OB.GtIsGt, 0); OB += "<"; Params.printWithComma(OB); - if (OB.back() == '>') - OB += " "; OB += ">"; } }; @@ -1402,38 +1426,38 @@ struct ForwardTemplateReference : Node { bool hasRHSComponentSlow(OutputBuffer &OB) const override { if (Printing) return false; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); return Ref->hasRHSComponent(OB); } bool hasArraySlow(OutputBuffer &OB) const override { if (Printing) return false; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); return Ref->hasArray(OB); } bool hasFunctionSlow(OutputBuffer &OB) const override { if (Printing) return false; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); return Ref->hasFunction(OB); } const Node *getSyntaxNode(OutputBuffer &OB) const override { if (Printing) return this; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); return Ref->getSyntaxNode(OB); } void printLeft(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); Ref->printLeft(OB); } void printRight(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore SavePrinting(Printing, true); + ScopedOverride SavePrinting(Printing, true); Ref->printRight(OB); } }; @@ -1473,21 +1497,6 @@ public: } }; -struct StdQualifiedName : Node { - Node *Child; - - StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} - - template void match(Fn F) const { F(Child); } - - StringView getBaseName() const override { return Child->getBaseName(); } - - void printLeft(OutputBuffer &OB) const override { - OB += "std::"; - Child->print(OB); - } -}; - enum class SpecialSubKind { allocator, basic_string, @@ -1497,15 +1506,25 @@ enum class SpecialSubKind { iostream, }; -class ExpandedSpecialSubstitution final : public Node { +class SpecialSubstitution; +class ExpandedSpecialSubstitution : public Node { +protected: SpecialSubKind SSK; + ExpandedSpecialSubstitution(SpecialSubKind SSK_, Kind K_) + : Node(K_), SSK(SSK_) {} public: ExpandedSpecialSubstitution(SpecialSubKind SSK_) - : Node(KExpandedSpecialSubstitution), SSK(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); + } + StringView getBaseName() const override { switch (SSK) { case SpecialSubKind::allocator: @@ -1524,82 +1543,44 @@ public: DEMANGLE_UNREACHABLE; } +private: void printLeft(OutputBuffer &OB) const override { - switch (SSK) { - case SpecialSubKind::allocator: - OB += "std::allocator"; - break; - case SpecialSubKind::basic_string: - OB += "std::basic_string"; - break; - case SpecialSubKind::string: - OB += "std::basic_string, " - "std::allocator >"; - break; - case SpecialSubKind::istream: - OB += "std::basic_istream >"; - break; - case SpecialSubKind::ostream: - OB += "std::basic_ostream >"; - break; - case SpecialSubKind::iostream: - OB += "std::basic_iostream >"; - break; + OB << "std::" << getBaseName(); + if (isInstantiation()) { + OB << ""; + if (SSK == SpecialSubKind::string) + OB << ", std::allocator"; + OB << ">"; } } }; -class SpecialSubstitution final : public Node { +class SpecialSubstitution final : public ExpandedSpecialSubstitution { public: - SpecialSubKind SSK; - SpecialSubstitution(SpecialSubKind SSK_) - : Node(KSpecialSubstitution), SSK(SSK_) {} + : ExpandedSpecialSubstitution(SSK_, KSpecialSubstitution) {} template void match(Fn F) const { F(SSK); } StringView getBaseName() const override { - switch (SSK) { - case SpecialSubKind::allocator: - return StringView("allocator"); - case SpecialSubKind::basic_string: - return StringView("basic_string"); - case SpecialSubKind::string: - return StringView("string"); - case SpecialSubKind::istream: - return StringView("istream"); - case SpecialSubKind::ostream: - return StringView("ostream"); - case SpecialSubKind::iostream: - return StringView("iostream"); + auto SV = ExpandedSpecialSubstitution::getBaseName (); + if (isInstantiation()) { + // The instantiations are typedefs that drop the "basic_" prefix. + assert(SV.startsWith("basic_")); + SV = SV.dropFront(sizeof("basic_") - 1); } - DEMANGLE_UNREACHABLE; + return SV; } void printLeft(OutputBuffer &OB) const override { - switch (SSK) { - case SpecialSubKind::allocator: - OB += "std::allocator"; - break; - case SpecialSubKind::basic_string: - OB += "std::basic_string"; - break; - case SpecialSubKind::string: - OB += "std::string"; - break; - case SpecialSubKind::istream: - OB += "std::istream"; - break; - case SpecialSubKind::ostream: - OB += "std::ostream"; - break; - case SpecialSubKind::iostream: - OB += "std::iostream"; - break; - } + OB << "std::" << getBaseName(); } }; +inline ExpandedSpecialSubstitution::ExpandedSpecialSubstitution( + SpecialSubstitution const *SS) + : ExpandedSpecialSubstitution(SS->SSK) {} + class CtorDtorName final : public Node { const Node *Basename; const bool IsDtor; @@ -1665,13 +1646,14 @@ public: void printDeclarator(OutputBuffer &OB) const { if (!TemplateParams.empty()) { + ScopedOverride LT(OB.GtIsGt, 0); OB += "<"; TemplateParams.printWithComma(OB); OB += ">"; } - OB += "("; + OB.printOpen(); Params.printWithComma(OB); - OB += ")"; + OB.printClose(); } void printLeft(OutputBuffer &OB) const override { @@ -1691,9 +1673,9 @@ public: template void match(Fn F) const { F(Bindings); } void printLeft(OutputBuffer &OB) const override { - OB += '['; + OB.printOpen('['); Bindings.printWithComma(OB); - OB += ']'; + OB.printClose(']'); } }; @@ -1705,28 +1687,31 @@ class BinaryExpr : public Node { const Node *RHS; public: - BinaryExpr(const Node *LHS_, StringView InfixOperator_, const Node *RHS_) - : Node(KBinaryExpr), LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) { + BinaryExpr(const Node *LHS_, StringView 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()); } - template void match(Fn F) const { F(LHS, InfixOperator, RHS); } - void printLeft(OutputBuffer &OB) const override { - // might be a template argument expression, then we need to disambiguate - // with parens. - if (InfixOperator == ">") - OB += "("; - - OB += "("; - LHS->print(OB); - OB += ") "; + 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->print(OB); - OB += ")"; - - if (InfixOperator == ">") - OB += ")"; + OB += " "; + RHS->printAsOperand(OB, getPrecedence(), IsAssign); + if (ParenAll) + OB.printClose(); } }; @@ -1735,17 +1720,18 @@ class ArraySubscriptExpr : public Node { const Node *Op2; public: - ArraySubscriptExpr(const Node *Op1_, const Node *Op2_) - : Node(KArraySubscriptExpr), Op1(Op1_), Op2(Op2_) {} + 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); } + template void match(Fn F) const { + F(Op1, Op2, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { - OB += "("; - Op1->print(OB); - OB += ")["; - Op2->print(OB); - OB += "]"; + Op1->printAsOperand(OB, getPrecedence()); + OB.printOpen('['); + Op2->printAsOperand(OB); + OB.printClose(']'); } }; @@ -1754,15 +1740,15 @@ class PostfixExpr : public Node { const StringView Operator; public: - PostfixExpr(const Node *Child_, StringView Operator_) - : Node(KPostfixExpr), Child(Child_), Operator(Operator_) {} + PostfixExpr(const Node *Child_, StringView Operator_, Prec Prec_) + : Node(KPostfixExpr, Prec_), Child(Child_), Operator(Operator_) {} - template void match(Fn F) const { F(Child, Operator); } + template void match(Fn F) const { + F(Child, Operator, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { - OB += "("; - Child->print(OB); - OB += ")"; + Child->printAsOperand(OB, getPrecedence(), true); OB += Operator; } }; @@ -1773,19 +1759,20 @@ class ConditionalExpr : public Node { const Node *Else; public: - ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_) - : Node(KConditionalExpr), Cond(Cond_), Then(Then_), Else(Else_) {} + 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); } + template void match(Fn F) const { + F(Cond, Then, Else, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { - OB += "("; - Cond->print(OB); - OB += ") ? ("; - Then->print(OB); - OB += ") : ("; - Else->print(OB); - OB += ")"; + Cond->printAsOperand(OB, getPrecedence()); + OB += " ? "; + Then->printAsOperand(OB); + OB += " : "; + Else->printAsOperand(OB, Prec::Assign, true); } }; @@ -1795,15 +1782,17 @@ class MemberExpr : public Node { const Node *RHS; public: - MemberExpr(const Node *LHS_, StringView Kind_, const Node *RHS_) - : Node(KMemberExpr), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + MemberExpr(const Node *LHS_, StringView 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); } + template void match(Fn F) const { + F(LHS, Kind, RHS, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { - LHS->print(OB); + LHS->printAsOperand(OB, getPrecedence(), true); OB += Kind; - RHS->print(OB); + RHS->printAsOperand(OB, getPrecedence(), false); } }; @@ -1847,15 +1836,19 @@ class EnclosingExpr : public Node { const StringView Postfix; public: - EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_) - : Node(KEnclosingExpr), Prefix(Prefix_), Infix(Infix_), - Postfix(Postfix_) {} + EnclosingExpr(StringView Prefix_, const Node *Infix_, + Prec Prec_ = Prec::Primary) + : Node(KEnclosingExpr, Prec_), Prefix(Prefix_), Infix(Infix_) {} - template void match(Fn F) const { F(Prefix, Infix, Postfix); } + 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; } }; @@ -1867,18 +1860,24 @@ class CastExpr : public Node { const Node *From; public: - CastExpr(StringView CastKind_, const Node *To_, const Node *From_) - : Node(KCastExpr), CastKind(CastKind_), To(To_), From(From_) {} + CastExpr(StringView 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); } + template void match(Fn F) const { + F(CastKind, To, From, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { OB += CastKind; - OB += "<"; - To->printLeft(OB); - OB += ">("; - From->printLeft(OB); - OB += ")"; + { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "<"; + To->printLeft(OB); + OB += ">"; + } + OB.printOpen(); + From->printAsOperand(OB); + OB.printClose(); } }; @@ -1892,10 +1891,11 @@ public: template void match(Fn F) const { F(Pack); } void printLeft(OutputBuffer &OB) const override { - OB += "sizeof...("; + OB += "sizeof..."; + OB.printOpen(); ParameterPackExpansion PPE(Pack); PPE.printLeft(OB); - OB += ")"; + OB.printClose(); } }; @@ -1904,16 +1904,18 @@ class CallExpr : public Node { NodeArray Args; public: - CallExpr(const Node *Callee_, NodeArray Args_) - : Node(KCallExpr), Callee(Callee_), Args(Args_) {} + CallExpr(const Node *Callee_, NodeArray Args_, Prec Prec_) + : Node(KCallExpr, Prec_), Callee(Callee_), Args(Args_) {} - template void match(Fn F) const { F(Callee, Args); } + template void match(Fn F) const { + F(Callee, Args, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { Callee->print(OB); - OB += "("; + OB.printOpen(); Args.printWithComma(OB); - OB += ")"; + OB.printClose(); } }; @@ -1926,31 +1928,31 @@ class NewExpr : public Node { bool IsArray; // new[] ? public: NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_, - bool IsArray_) - : Node(KNewExpr), ExprList(ExprList_), Type(Type_), InitList(InitList_), - IsGlobal(IsGlobal_), IsArray(IsArray_) {} + 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); + F(ExprList, Type, InitList, IsGlobal, IsArray, getPrecedence()); } void printLeft(OutputBuffer &OB) const override { if (IsGlobal) - OB += "::operator "; + OB += "::"; OB += "new"; if (IsArray) OB += "[]"; - OB += ' '; if (!ExprList.empty()) { - OB += "("; + OB.printOpen(); ExprList.printWithComma(OB); - OB += ")"; + OB.printClose(); } + OB += " "; Type->print(OB); if (!InitList.empty()) { - OB += "("; + OB.printOpen(); InitList.printWithComma(OB); - OB += ")"; + OB.printClose(); } } }; @@ -1961,17 +1963,21 @@ class DeleteExpr : public Node { bool IsArray; public: - DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_) - : Node(KDeleteExpr), Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} + 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); } + 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 += "[]"; + OB += ' '; Op->print(OB); } }; @@ -1981,16 +1987,16 @@ class PrefixExpr : public Node { Node *Child; public: - PrefixExpr(StringView Prefix_, Node *Child_) - : Node(KPrefixExpr), Prefix(Prefix_), Child(Child_) {} + PrefixExpr(StringView Prefix_, Node *Child_, Prec Prec_) + : Node(KPrefixExpr, Prec_), Prefix(Prefix_), Child(Child_) {} - template void match(Fn F) const { F(Prefix, Child); } + template void match(Fn F) const { + F(Prefix, Child, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { OB += Prefix; - OB += "("; - Child->print(OB); - OB += ")"; + Child->printAsOperand(OB, getPrecedence()); } }; @@ -2013,17 +2019,20 @@ class ConversionExpr : public Node { NodeArray Expressions; public: - ConversionExpr(const Node *Type_, NodeArray Expressions_) - : Node(KConversionExpr), Type(Type_), Expressions(Expressions_) {} + ConversionExpr(const Node *Type_, NodeArray Expressions_, Prec Prec_) + : Node(KConversionExpr, Prec_), Type(Type_), Expressions(Expressions_) {} - template void match(Fn F) const { F(Type, Expressions); } + template void match(Fn F) const { + F(Type, Expressions, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { - OB += "("; + OB.printOpen(); Type->print(OB); - OB += ")("; + OB.printClose(); + OB.printOpen(); Expressions.printWithComma(OB); - OB += ")"; + OB.printClose(); } }; @@ -2034,18 +2043,21 @@ class PointerToMemberConversionExpr : public Node { public: PointerToMemberConversionExpr(const Node *Type_, const Node *SubExpr_, - StringView Offset_) - : Node(KPointerToMemberConversionExpr), Type(Type_), SubExpr(SubExpr_), - Offset(Offset_) {} + StringView Offset_, Prec Prec_) + : Node(KPointerToMemberConversionExpr, Prec_), Type(Type_), + SubExpr(SubExpr_), Offset(Offset_) {} - template void match(Fn F) const { F(Type, SubExpr, Offset); } + template void match(Fn F) const { + F(Type, SubExpr, Offset, getPrecedence()); + } void printLeft(OutputBuffer &OB) const override { - OB += "("; + OB.printOpen(); Type->print(OB); - OB += ")("; + OB.printClose(); + OB.printOpen(); SubExpr->print(OB); - OB += ")"; + OB.printClose(); } }; @@ -2131,41 +2143,33 @@ public: void printLeft(OutputBuffer &OB) const override { auto PrintPack = [&] { - OB += '('; + OB.printOpen(); ParameterPackExpansion(Pack).print(OB); - OB += ')'; + OB.printClose(); }; - OB += '('; - - if (IsLeftFold) { - // init op ... op pack - if (Init != nullptr) { - Init->print(OB); - OB += ' '; - OB += OperatorName; - OB += ' '; - } - // ... op pack - OB += "... "; - OB += OperatorName; - OB += ' '; - PrintPack(); - } else { // !IsLeftFold - // pack op ... - PrintPack(); - OB += ' '; - OB += OperatorName; - OB += " ..."; - // pack op ... op init - if (Init != nullptr) { - OB += ' '; - OB += OperatorName; - OB += ' '; - Init->print(OB); - } + 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 += ')'; + OB << "..."; + if (IsLeftFold || Init != nullptr) { + // ' op (init|pack)' + OB << " " << OperatorName << " "; + if (IsLeftFold) + PrintPack(); + else + Init->printAsOperand(OB, Prec::Cast, true); + } + OB.printClose(); } }; @@ -2239,9 +2243,9 @@ public: template void match(Fn F) const { F(Ty, Integer); } void printLeft(OutputBuffer &OB) const override { - OB << "("; + OB.printOpen(); Ty->print(OB); - OB << ")"; + OB.printClose(); if (Integer[0] == 'n') OB << "-" << Integer.dropFront(1); @@ -2262,13 +2266,13 @@ public: void printLeft(OutputBuffer &OB) const override { if (Type.size() > 3) { - OB += "("; + OB.printOpen(); OB += Type; - OB += ")"; + OB.printClose(); } if (Value[0] == 'n') { - OB += "-"; + OB += '-'; OB += Value.dropFront(1); } else OB += Value; @@ -2344,24 +2348,22 @@ using LongDoubleLiteral = FloatLiteralImpl; template void Node::visit(Fn F) const { switch (K) { -#define CASE(X) case K ## X: return F(static_cast(this)); - FOR_EACH_NODE_KIND(CASE) -#undef CASE +#define NODE(X) \ + case K##X: \ + return F(static_cast(this)); +#include "ItaniumNodes.def" } assert(0 && "unknown mangling node kind"); } /// Determine the kind of a node from its type. template struct NodeKind; -#define SPECIALIZATION(X) \ - template<> struct NodeKind { \ - static constexpr Node::Kind Kind = Node::K##X; \ - static constexpr const char *name() { return #X; } \ +#define NODE(X) \ + template <> struct NodeKind { \ + static constexpr Node::Kind Kind = Node::K##X; \ + static constexpr const char *name() { return #X; } \ }; -FOR_EACH_NODE_KIND(SPECIALIZATION) -#undef SPECIALIZATION - -#undef FOR_EACH_NODE_KIND +#include "ItaniumNodes.def" template struct AbstractManglingParser { const char *First; @@ -2499,17 +2501,16 @@ template struct AbstractManglingParser { /// Parse the production. Node *parseExpr(); - Node *parsePrefixExpr(StringView Kind); - Node *parseBinaryExpr(StringView Kind); + Node *parsePrefixExpr(StringView Kind, Node::Prec Prec); + Node *parseBinaryExpr(StringView Kind, Node::Prec Prec); Node *parseIntegerLiteral(StringView Lit); Node *parseExprPrimary(); template Node *parseFloatingLiteral(); Node *parseFunctionParam(); - Node *parseNewExpr(); Node *parseConversionExpr(); Node *parseBracedExpr(); Node *parseFoldExpr(); - Node *parsePointerToMemberConversionExpr(); + Node *parsePointerToMemberConversionExpr(Node::Prec Prec); Node *parseSubobjectExpr(); /// Parse the production. @@ -2557,17 +2558,80 @@ template struct AbstractManglingParser { Node *parseName(NameState *State = nullptr); Node *parseLocalName(NameState *State); Node *parseOperatorName(NameState *State); - Node *parseUnqualifiedName(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); + 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: + StringView getSymbol() const { + StringView Res = Name; + if (Kind < Unnameable) { + assert(Res.startsWith("operator") && + "operator name does not start with 'operator'"); + Res = Res.dropFront(sizeof("operator") - 1); + Res.consumeFront(' '); + } + return Res; + } + StringView 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(); + Node *parseUnresolvedName(bool Global); Node *parseSimpleId(); Node *parseBaseUnresolvedName(); Node *parseUnresolvedType(); @@ -2588,26 +2652,16 @@ const char* parse_discriminator(const char* first, const char* last); // ::= template Node *AbstractManglingParser::parseName(NameState *State) { - consumeIf('L'); // extension - if (look() == 'N') return getDerived().parseNestedName(State); if (look() == 'Z') return getDerived().parseLocalName(State); Node *Result = nullptr; - bool IsSubst = look() == 'S' && look(1) != 't'; - if (IsSubst) { - // A substitution must lead to: - // ::= - Result = getDerived().parseSubstitution(); - } else { - // An unscoped name can be one of: - // ::= - // ::= - Result = getDerived().parseUnscopedName(State); - } - if (Result == nullptr) + bool IsSubst = false; + + Result = getDerived().parseUnscopedName(State, &IsSubst); + if (!Result) return nullptr; if (look() == 'I') { @@ -2667,38 +2721,63 @@ Node *AbstractManglingParser::parseLocalName(NameState *State) { // ::= // ::= St # ::std:: -// extension ::= StL +// [*] extension template Node * -AbstractManglingParser::parseUnscopedName(NameState *State) { - bool IsStd = consumeIf("St"); - if (IsStd) - consumeIf('L'); - - Node *Result = getDerived().parseUnqualifiedName(State); - if (Result == nullptr) - return nullptr; - if (IsStd) - Result = make(Result); - - return Result; +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; } -// ::= [abi-tags] -// ::= -// ::= -// ::= -// ::= DC + E # structured binding declaration +// ::= [] L? [] +// ::= [] [] +// ::= [] L? [] +// ::= [] L? [] +// # structured binding declaration +// ::= [] L? DC + E template -Node * -AbstractManglingParser::parseUnqualifiedName(NameState *State) { - // s are special-cased in parseNestedName(). +Node *AbstractManglingParser::parseUnqualifiedName( + NameState *State, Node *Scope, ModuleName *Module) { + if (getDerived().parseModuleNameOpt(Module)) + return nullptr; + + consumeIf('L'); + Node *Result; - if (look() == 'U') - Result = getDerived().parseUnnamedTypeName(State); - else if (look() >= '1' && look() <= '9') + if (look() >= '1' && look() <= '9') { Result = getDerived().parseSourceName(State); - else if (consumeIf("DC")) { + } 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); @@ -2707,13 +2786,46 @@ AbstractManglingParser::parseUnqualifiedName(NameState *State) { Names.push_back(Binding); } while (!consumeIf('E')); Result = make(popTrailingNodeArray(BindingsBegin)); - } else + } 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 [] _ // ::= // @@ -2735,7 +2847,7 @@ AbstractManglingParser::parseUnnamedTypeName(NameState *State) { return make(Count); } if (consumeIf("Ul")) { - SwapAndRestore SwapParams(ParsingLambdaParamsAtLevel, + ScopedOverride SwapParams(ParsingLambdaParamsAtLevel, TemplateParams.size()); ScopedTemplateParamList LambdaTemplateParams(this); @@ -2813,97 +2925,124 @@ Node *AbstractManglingParser::parseSourceName(NameState *) { return make(Name); } -// ::= aa # && -// ::= ad # & (unary) -// ::= an # & -// ::= aN # &= -// ::= aS # = -// ::= cl # () -// ::= cm # , -// ::= co # ~ -// ::= cv # (cast) -// ::= da # delete[] -// ::= de # * (unary) -// ::= dl # delete -// ::= dv # / -// ::= dV # /= -// ::= eo # ^ -// ::= eO # ^= -// ::= eq # == -// ::= ge # >= -// ::= gt # > -// ::= ix # [] -// ::= le # <= +// 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; + + auto Op = std::lower_bound( + &Ops[0], &Ops[NumOps], First, + [](const OperatorInfo &Op_, const char *Enc_) { return Op_ < Enc_; }); + if (Op == &Ops[NumOps] || *Op != First) + return nullptr; + + First += 2; + return Op; +} + +// ::= See parseOperatorEncoding() // ::= li # operator "" -// ::= ls # << -// ::= lS # <<= -// ::= lt # < -// ::= mi # - -// ::= mI # -= -// ::= ml # * -// ::= mL # *= -// ::= mm # -- (postfix in context) -// ::= na # new[] -// ::= ne # != -// ::= ng # - (unary) -// ::= nt # ! -// ::= nw # new -// ::= oo # || -// ::= or # | -// ::= oR # |= -// ::= pm # ->* -// ::= pl # + -// ::= pL # += -// ::= pp # ++ (postfix in context) -// ::= ps # + (unary) -// ::= pt # -> -// ::= qu # ? -// ::= rm # % -// ::= rM # %= -// ::= rs # >> -// ::= rS # >>= -// ::= ss # <=> C++2a -// ::= v # vendor extended operator +// ::= v # vendor extended operator template Node * AbstractManglingParser::parseOperatorName(NameState *State) { - switch (look()) { - case 'a': - switch (look(1)) { - case 'a': - First += 2; - return make("operator&&"); - case 'd': - case 'n': - First += 2; - return make("operator&"); - case 'N': - First += 2; - return make("operator&="); - case 'S': - First += 2; - return make("operator="); - } - return nullptr; - case 'c': - switch (look(1)) { - case 'l': - First += 2; - return make("operator()"); - case 'm': - First += 2; - return make("operator,"); - case 'o': - First += 2; - return make("operator~"); - // ::= cv # (cast) - case 'v': { - First += 2; - SwapAndRestore SaveTemplate(TryToParseTemplateArgs, false); + 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. - SwapAndRestore SavePermit(PermitForwardTemplateReferences, + ScopedOverride SavePermit(PermitForwardTemplateReferences, PermitForwardTemplateReferences || State != nullptr); Node *Ty = getDerived().parseType(); @@ -2912,185 +3051,29 @@ AbstractManglingParser::parseOperatorName(NameState *State) { if (State) State->CtorDtorConversion = true; return make(Ty); } - } - return nullptr; - case 'd': - switch (look(1)) { - case 'a': - First += 2; - return make("operator delete[]"); - case 'e': - First += 2; - return make("operator*"); - case 'l': - First += 2; - return make("operator delete"); - case 'v': - First += 2; - return make("operator/"); - case 'V': - First += 2; - return make("operator/="); - } - return nullptr; - case 'e': - switch (look(1)) { - case 'o': - First += 2; - return make("operator^"); - case 'O': - First += 2; - return make("operator^="); - case 'q': - First += 2; - return make("operator=="); - } - return nullptr; - case 'g': - switch (look(1)) { - case 'e': - First += 2; - return make("operator>="); - case 't': - First += 2; - return make("operator>"); - } - return nullptr; - case 'i': - if (look(1) == 'x') { - First += 2; - return make("operator[]"); - } - return nullptr; - case 'l': - switch (look(1)) { - case 'e': - First += 2; - return make("operator<="); + + 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 "" - case 'i': { - First += 2; - Node *SN = getDerived().parseSourceName(State); - if (SN == nullptr) - return nullptr; - return make(SN); - } - case 's': - First += 2; - return make("operator<<"); - case 'S': - First += 2; - return make("operator<<="); - case 't': - First += 2; - return make("operator<"); - } - return nullptr; - case 'm': - switch (look(1)) { - case 'i': - First += 2; - return make("operator-"); - case 'I': - First += 2; - return make("operator-="); - case 'l': - First += 2; - return make("operator*"); - case 'L': - First += 2; - return make("operator*="); - case 'm': - First += 2; - return make("operator--"); - } - return nullptr; - case 'n': - switch (look(1)) { - case 'a': - First += 2; - return make("operator new[]"); - case 'e': - First += 2; - return make("operator!="); - case 'g': - First += 2; - return make("operator-"); - case 't': - First += 2; - return make("operator!"); - case 'w': - First += 2; - return make("operator new"); - } - return nullptr; - case 'o': - switch (look(1)) { - case 'o': - First += 2; - return make("operator||"); - case 'r': - First += 2; - return make("operator|"); - case 'R': - First += 2; - return make("operator|="); - } - return nullptr; - case 'p': - switch (look(1)) { - case 'm': - First += 2; - return make("operator->*"); - case 'l': - First += 2; - return make("operator+"); - case 'L': - First += 2; - return make("operator+="); - case 'p': - First += 2; - return make("operator++"); - case 's': - First += 2; - return make("operator+"); - case 't': - First += 2; - return make("operator->"); - } - return nullptr; - case 'q': - if (look(1) == 'u') { - First += 2; - return make("operator?"); - } - return nullptr; - case 'r': - switch (look(1)) { - case 'm': - First += 2; - return make("operator%"); - case 'M': - First += 2; - return make("operator%="); - case 's': - First += 2; - return make("operator>>"); - case 'S': - First += 2; - return make("operator>>="); - } - return nullptr; - case 's': - if (look(1) == 's') { - First += 2; - return make("operator<=>"); - } - return nullptr; - // ::= v # vendor extended operator - case 'v': - if (std::isdigit(look(1))) { - First += 2; + 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; @@ -3098,6 +3081,7 @@ AbstractManglingParser::parseOperatorName(NameState *State) { } return nullptr; } + return nullptr; } @@ -3116,19 +3100,11 @@ Node * AbstractManglingParser::parseCtorDtorName(Node *&SoFar, NameState *State) { if (SoFar->getKind() == Node::KSpecialSubstitution) { - auto SSK = static_cast(SoFar)->SSK; - switch (SSK) { - case SpecialSubKind::string: - case SpecialSubKind::istream: - case SpecialSubKind::ostream: - case SpecialSubKind::iostream: - SoFar = make(SSK); - if (!SoFar) - return nullptr; - break; - default: - break; - } + // Expand the special substitution. + SoFar = make( + static_cast(SoFar)); + if (!SoFar) + return nullptr; } if (consumeIf('C')) { @@ -3157,8 +3133,10 @@ AbstractManglingParser::parseCtorDtorName(Node *&SoFar, return nullptr; } -// ::= N [] [] E -// ::= N [] [] E +// ::= N [] [] +// E +// ::= N [] [] +// E // // ::= // ::= @@ -3167,7 +3145,7 @@ AbstractManglingParser::parseCtorDtorName(Node *&SoFar, // ::= # empty // ::= // ::= -// extension ::= L +// [*] extension // // := [] M // @@ -3187,90 +3165,76 @@ AbstractManglingParser::parseNestedName(NameState *State) { if (State) State->ReferenceQualifier = FrefQualRValue; } else if (consumeIf('R')) { if (State) State->ReferenceQualifier = FrefQualLValue; - } else + } else { if (State) State->ReferenceQualifier = FrefQualNone; - - Node *SoFar = nullptr; - auto PushComponent = [&](Node *Comp) { - if (!Comp) return false; - if (SoFar) SoFar = make(SoFar, Comp); - else SoFar = Comp; - if (State) State->EndsWithTemplateArgs = false; - return SoFar != nullptr; - }; - - if (consumeIf("St")) { - SoFar = make("std"); - if (!SoFar) - return nullptr; } + Node *SoFar = nullptr; while (!consumeIf('E')) { - consumeIf('L'); // extension + if (State) + // Only set end-with-template on the case that does that. + State->EndsWithTemplateArgs = false; - // := [] M - if (consumeIf('M')) { - if (SoFar == nullptr) - return nullptr; - continue; - } - - // ::= if (look() == 'T') { - if (!PushComponent(getDerived().parseTemplateParam())) - return nullptr; - Subs.push_back(SoFar); - continue; - } - - // ::= - if (look() == 'I') { + // ::= + if (SoFar != nullptr) + return nullptr; // Cannot have a prefix. + SoFar = getDerived().parseTemplateParam(); + } else if (look() == 'I') { + // ::= + if (SoFar == nullptr) + return nullptr; // Must have a prefix. Node *TA = getDerived().parseTemplateArgs(State != nullptr); - if (TA == nullptr || SoFar == nullptr) + if (TA == nullptr) return nullptr; + if (SoFar->getKind() == Node::KNameWithTemplateArgs) + // Semantically cannot be generated by a + // C++ entity. There will always be [something like] a name between + // them. + return nullptr; + if (State) + State->EndsWithTemplateArgs = true; SoFar = make(SoFar, TA); - if (!SoFar) - return nullptr; - if (State) State->EndsWithTemplateArgs = true; - Subs.push_back(SoFar); - continue; - } - - // ::= - if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) { - if (!PushComponent(getDerived().parseDecltype())) - return nullptr; - Subs.push_back(SoFar); - continue; - } + } else if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) { + // ::= + if (SoFar != nullptr) + return nullptr; // Cannot have a prefix. + SoFar = getDerived().parseDecltype(); + } else { + ModuleName *Module = nullptr; - // ::= - if (look() == 'S' && look(1) != 't') { - Node *S = getDerived().parseSubstitution(); - if (!PushComponent(S)) - return nullptr; - if (SoFar != S) - Subs.push_back(S); - continue; - } + if (look() == 'S') { + // ::= + Node *S = nullptr; + if (look(1) == 't') { + First += 2; + S = make("std"); + } else { + S = getDerived().parseSubstitution(); + } + if (!S) + return nullptr; + if (S->getKind() == Node::KModuleName) { + Module = static_cast(S); + } else if (SoFar != nullptr) { + return nullptr; // Cannot have a prefix. + } else { + SoFar = S; + continue; // Do not push a new substitution. + } + } - // Parse an thats actually a . - if (look() == 'C' || (look() == 'D' && look(1) != 'C')) { - if (SoFar == nullptr) - return nullptr; - if (!PushComponent(getDerived().parseCtorDtorName(SoFar, State))) - return nullptr; - SoFar = getDerived().parseAbiTags(SoFar); - if (SoFar == nullptr) - return nullptr; - Subs.push_back(SoFar); - continue; + // ::= [] + SoFar = getDerived().parseUnqualifiedName(State, SoFar, Module); } - // ::= - if (!PushComponent(getDerived().parseUnqualifiedName(State))) + if (SoFar == nullptr) return nullptr; Subs.push_back(SoFar); + + // No longer used. + // := [] M + consumeIf('M'); } if (SoFar == nullptr || Subs.empty()) @@ -3365,6 +3329,7 @@ Node *AbstractManglingParser::parseBaseUnresolvedName() { // ::= [gs] # x or (with "gs") ::x // ::= [gs] sr + E // # A::x, N::y, A::z; "gs" means leading "::" +// [gs] has been parsed by caller. // ::= sr # T::x / decltype(p)::x // extension ::= sr // # T::N::x /decltype(p)::N::x @@ -3372,7 +3337,7 @@ Node *AbstractManglingParser::parseBaseUnresolvedName() { // // ::= template -Node *AbstractManglingParser::parseUnresolvedName() { +Node *AbstractManglingParser::parseUnresolvedName(bool Global) { Node *SoFar = nullptr; // srN [] * E @@ -3406,8 +3371,6 @@ Node *AbstractManglingParser::parseUnresolvedName() { return make(SoFar, Base); } - bool Global = consumeIf("gs"); - // [gs] # x or (with "gs") ::x if (!consumeIf("sr")) { SoFar = getDerived().parseBaseUnresolvedName(); @@ -3637,7 +3600,7 @@ Node *AbstractManglingParser::parseDecltype() { return nullptr; if (!consumeIf('E')) return nullptr; - return make("decltype(", E, ")"); + return make("decltype", E); } // ::= A _ @@ -3723,8 +3686,8 @@ Node *AbstractManglingParser::parseQualifiedType() { StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto")); StringView Proto; { - SwapAndRestore SaveFirst(First, ProtoSourceName.begin()), - SaveLast(Last, ProtoSourceName.end()); + ScopedOverride SaveFirst(First, ProtoSourceName.begin()), + SaveLast(Last, ProtoSourceName.end()); Proto = parseBareSourceName(); } if (Proto.empty()) @@ -3929,6 +3892,22 @@ Node *AbstractManglingParser::parseType() { return nullptr; return make(DimensionNumber); } + // ::= DB _ # C23 signed _BitInt(N) + // ::= DB _ # C23 signed _BitInt(N) + // ::= DU _ # C23 unsigned _BitInt(N) + // ::= DU _ # C23 unsigned _BitInt(N) + case 'B': + case 'U': { + bool Signed = look(1) == 'B'; + First += 2; + Node *Size = std::isdigit(look()) ? make(parseNumber()) + : getDerived().parseExpr(); + if (!Size) + return nullptr; + if (!consumeIf('_')) + return nullptr; + return make(Size, Signed); + } // ::= Di # char32_t case 'i': First += 2; @@ -4077,8 +4056,9 @@ Node *AbstractManglingParser::parseType() { // ::= # See Compression below case 'S': { if (look(1) != 't') { - Result = getDerived().parseSubstitution(); - if (Result == nullptr) + bool IsSubst = false; + Result = getDerived().parseUnscopedName(nullptr, &IsSubst); + if (!Result) return nullptr; // Sub could be either of: @@ -4091,12 +4071,14 @@ Node *AbstractManglingParser::parseType() { // If this is followed by some , and we're permitted to // parse them, take the second production. - if (TryToParseTemplateArgs && look() == 'I') { + if (look() == 'I' && (!IsSubst || TryToParseTemplateArgs)) { + if (!IsSubst) + Subs.push_back(Result); Node *TA = getDerived().parseTemplateArgs(); if (TA == nullptr) return nullptr; Result = make(Result, TA); - } else { + } else if (IsSubst) { // If all we parsed was a substitution, don't re-insert into the // substitution table. return Result; @@ -4121,22 +4103,24 @@ Node *AbstractManglingParser::parseType() { } template -Node *AbstractManglingParser::parsePrefixExpr(StringView Kind) { +Node *AbstractManglingParser::parsePrefixExpr(StringView Kind, + Node::Prec Prec) { Node *E = getDerived().parseExpr(); if (E == nullptr) return nullptr; - return make(Kind, E); + return make(Kind, E, Prec); } template -Node *AbstractManglingParser::parseBinaryExpr(StringView Kind) { +Node *AbstractManglingParser::parseBinaryExpr(StringView Kind, + Node::Prec Prec) { Node *LHS = getDerived().parseExpr(); if (LHS == nullptr) return nullptr; Node *RHS = getDerived().parseExpr(); if (RHS == nullptr) return nullptr; - return make(LHS, Kind, RHS); + return make(LHS, Kind, RHS, Prec); } template @@ -4191,43 +4175,6 @@ Node *AbstractManglingParser::parseFunctionParam() { return nullptr; } -// [gs] nw * _ E # new (expr-list) type -// [gs] nw * _ # new (expr-list) type (init) -// [gs] na * _ E # new[] (expr-list) type -// [gs] na * _ # new[] (expr-list) type (init) -// ::= pi * E # parenthesized initialization -template -Node *AbstractManglingParser::parseNewExpr() { - bool Global = consumeIf("gs"); - bool IsArray = look(1) == 'a'; - if (!consumeIf("nw") && !consumeIf("na")) - return nullptr; - size_t Exprs = Names.size(); - while (!consumeIf('_')) { - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return nullptr; - Names.push_back(Ex); - } - NodeArray ExprList = popTrailingNodeArray(Exprs); - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return Ty; - if (consumeIf("pi")) { - size_t InitsBegin = Names.size(); - while (!consumeIf('E')) { - Node *Init = getDerived().parseExpr(); - if (Init == nullptr) - return Init; - Names.push_back(Init); - } - NodeArray Inits = popTrailingNodeArray(InitsBegin); - return make(ExprList, Ty, Inits, Global, IsArray); - } else if (!consumeIf('E')) - return nullptr; - return make(ExprList, Ty, NodeArray(), Global, IsArray); -} - // cv # conversion with one argument // cv _ * E # conversion with a different number of arguments template @@ -4236,7 +4183,7 @@ Node *AbstractManglingParser::parseConversionExpr() { return nullptr; Node *Ty; { - SwapAndRestore SaveTemp(TryToParseTemplateArgs, false); + ScopedOverride SaveTemp(TryToParseTemplateArgs, false); Ty = getDerived().parseType(); } @@ -4353,7 +4300,7 @@ Node *AbstractManglingParser::parseExprPrimary() { return nullptr; } case 'D': - if (consumeIf("DnE")) + if (consumeIf("Dn") && (consumeIf('0'), consumeIf('E'))) return make("nullptr"); return nullptr; case 'T': @@ -4440,55 +4387,38 @@ Node *AbstractManglingParser::parseFoldExpr() { if (!consumeIf('f')) return nullptr; - char FoldKind = look(); - bool IsLeftFold, HasInitializer; - HasInitializer = FoldKind == 'L' || FoldKind == 'R'; - if (FoldKind == 'l' || FoldKind == 'L') - IsLeftFold = true; - else if (FoldKind == 'r' || FoldKind == 'R') - IsLeftFold = false; - else + bool IsLeftFold = false, HasInitializer = false; + switch (look()) { + default: return nullptr; + case 'L': + IsLeftFold = true; + HasInitializer = true; + break; + case 'R': + HasInitializer = true; + break; + case 'l': + IsLeftFold = true; + break; + case 'r': + break; + } ++First; - // FIXME: This map is duplicated in parseOperatorName and parseExpr. - StringView OperatorName; - if (consumeIf("aa")) OperatorName = "&&"; - else if (consumeIf("an")) OperatorName = "&"; - else if (consumeIf("aN")) OperatorName = "&="; - else if (consumeIf("aS")) OperatorName = "="; - else if (consumeIf("cm")) OperatorName = ","; - else if (consumeIf("ds")) OperatorName = ".*"; - else if (consumeIf("dv")) OperatorName = "/"; - else if (consumeIf("dV")) OperatorName = "/="; - else if (consumeIf("eo")) OperatorName = "^"; - else if (consumeIf("eO")) OperatorName = "^="; - else if (consumeIf("eq")) OperatorName = "=="; - else if (consumeIf("ge")) OperatorName = ">="; - else if (consumeIf("gt")) OperatorName = ">"; - else if (consumeIf("le")) OperatorName = "<="; - else if (consumeIf("ls")) OperatorName = "<<"; - else if (consumeIf("lS")) OperatorName = "<<="; - else if (consumeIf("lt")) OperatorName = "<"; - else if (consumeIf("mi")) OperatorName = "-"; - else if (consumeIf("mI")) OperatorName = "-="; - else if (consumeIf("ml")) OperatorName = "*"; - else if (consumeIf("mL")) OperatorName = "*="; - else if (consumeIf("ne")) OperatorName = "!="; - else if (consumeIf("oo")) OperatorName = "||"; - else if (consumeIf("or")) OperatorName = "|"; - else if (consumeIf("oR")) OperatorName = "|="; - else if (consumeIf("pl")) OperatorName = "+"; - else if (consumeIf("pL")) OperatorName = "+="; - else if (consumeIf("rm")) OperatorName = "%"; - else if (consumeIf("rM")) OperatorName = "%="; - else if (consumeIf("rs")) OperatorName = ">>"; - else if (consumeIf("rS")) OperatorName = ">>="; - else return nullptr; + const auto *Op = parseOperatorEncoding(); + if (!Op) + return nullptr; + if (!(Op->getKind() == OperatorInfo::Binary + || (Op->getKind() == OperatorInfo::Member + && Op->getName().back() == '*'))) + return nullptr; - Node *Pack = getDerived().parseExpr(), *Init = nullptr; + Node *Pack = getDerived().parseExpr(); if (Pack == nullptr) return nullptr; + + Node *Init = nullptr; if (HasInitializer) { Init = getDerived().parseExpr(); if (Init == nullptr) @@ -4498,14 +4428,16 @@ Node *AbstractManglingParser::parseFoldExpr() { if (IsLeftFold && Init) std::swap(Pack, Init); - return make(IsLeftFold, OperatorName, Pack, Init); + return make(IsLeftFold, Op->getSymbol(), Pack, Init); } // ::= mc [] E // // Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/47 template -Node *AbstractManglingParser::parsePointerToMemberConversionExpr() { +Node * +AbstractManglingParser::parsePointerToMemberConversionExpr( + Node::Prec Prec) { Node *Ty = getDerived().parseType(); if (!Ty) return nullptr; @@ -4515,7 +4447,7 @@ Node *AbstractManglingParser::parsePointerToMemberConversionExpr StringView Offset = getDerived().parseNumber(true); if (!consumeIf('E')) return nullptr; - return make(Ty, Expr, Offset); + return make(Ty, Expr, Offset, Prec); } // ::= so [] * [p] E @@ -4592,466 +4524,250 @@ Node *AbstractManglingParser::parseSubobjectExpr() { template Node *AbstractManglingParser::parseExpr() { bool Global = consumeIf("gs"); + + const auto *Op = parseOperatorEncoding(); + if (Op) { + auto Sym = Op->getSymbol(); + switch (Op->getKind()) { + case OperatorInfo::Binary: + // Binary operator: lhs @ rhs + return getDerived().parseBinaryExpr(Sym, Op->getPrecedence()); + case OperatorInfo::Prefix: + // Prefix unary operator: @ expr + return getDerived().parsePrefixExpr(Sym, Op->getPrecedence()); + case OperatorInfo::Postfix: { + // Postfix unary operator: expr @ + if (consumeIf('_')) + return getDerived().parsePrefixExpr(Sym, Op->getPrecedence()); + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make(Ex, Sym, Op->getPrecedence()); + } + case OperatorInfo::Array: { + // Array Index: lhs [ rhs ] + Node *Base = getDerived().parseExpr(); + if (Base == nullptr) + return nullptr; + Node *Index = getDerived().parseExpr(); + if (Index == nullptr) + return nullptr; + return make(Base, Index, Op->getPrecedence()); + } + case OperatorInfo::Member: { + // Member access lhs @ rhs + Node *LHS = getDerived().parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = getDerived().parseExpr(); + if (RHS == nullptr) + return nullptr; + return make(LHS, Sym, RHS, Op->getPrecedence()); + } + case OperatorInfo::New: { + // New + // # new (expr-list) type [(init)] + // [gs] nw * _ [pi *] E + // # new[] (expr-list) type [(init)] + // [gs] na * _ [pi *] E + size_t Exprs = Names.size(); + while (!consumeIf('_')) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + Names.push_back(Ex); + } + NodeArray ExprList = popTrailingNodeArray(Exprs); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + bool HaveInits = consumeIf("pi"); + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + if (!HaveInits) + return nullptr; + Node *Init = getDerived().parseExpr(); + if (Init == nullptr) + return Init; + Names.push_back(Init); + } + NodeArray Inits = popTrailingNodeArray(InitsBegin); + return make(ExprList, Ty, Inits, Global, + /*IsArray=*/Op->getFlag(), Op->getPrecedence()); + } + case OperatorInfo::Del: { + // Delete + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make(Ex, Global, /*IsArray=*/Op->getFlag(), + Op->getPrecedence()); + } + case OperatorInfo::Call: { + // Function Call + Node *Callee = getDerived().parseExpr(); + if (Callee == nullptr) + return nullptr; + size_t ExprsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return nullptr; + Names.push_back(E); + } + return make(Callee, popTrailingNodeArray(ExprsBegin), + Op->getPrecedence()); + } + case OperatorInfo::CCast: { + // C Cast: (type)expr + Node *Ty; + { + ScopedOverride SaveTemp(TryToParseTemplateArgs, false); + Ty = getDerived().parseType(); + } + if (Ty == nullptr) + return nullptr; + + size_t ExprsBegin = Names.size(); + bool IsMany = consumeIf('_'); + while (!consumeIf('E')) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return E; + Names.push_back(E); + if (!IsMany) + break; + } + NodeArray Exprs = popTrailingNodeArray(ExprsBegin); + if (!IsMany && Exprs.size() != 1) + return nullptr; + return make(Ty, Exprs, Op->getPrecedence()); + } + case OperatorInfo::Conditional: { + // Conditional operator: expr ? expr : expr + Node *Cond = getDerived().parseExpr(); + if (Cond == nullptr) + return nullptr; + Node *LHS = getDerived().parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = getDerived().parseExpr(); + if (RHS == nullptr) + return nullptr; + return make(Cond, LHS, RHS, Op->getPrecedence()); + } + case OperatorInfo::NamedCast: { + // Named cast operation, @(expr) + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make(Sym, Ty, Ex, Op->getPrecedence()); + } + case OperatorInfo::OfIdOp: { + // [sizeof/alignof/typeid] ( | ) + Node *Arg = + Op->getFlag() ? getDerived().parseType() : getDerived().parseExpr(); + if (!Arg) + return nullptr; + return make(Sym, Arg, Op->getPrecedence()); + } + case OperatorInfo::NameOnly: { + // Not valid as an expression operand. + return nullptr; + } + } + DEMANGLE_UNREACHABLE; + } + if (numLeft() < 2) return nullptr; - switch (*First) { - case 'L': + if (look() == 'L') return getDerived().parseExprPrimary(); - case 'T': + if (look() == 'T') return getDerived().parseTemplateParam(); - case 'f': { + if (look() == 'f') { // Disambiguate a fold expression from a . if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2)))) return getDerived().parseFunctionParam(); return getDerived().parseFoldExpr(); } - case 'a': - switch (First[1]) { - case 'a': - First += 2; - return getDerived().parseBinaryExpr("&&"); - case 'd': - First += 2; - return getDerived().parsePrefixExpr("&"); - case 'n': - First += 2; - return getDerived().parseBinaryExpr("&"); - case 'N': - First += 2; - return getDerived().parseBinaryExpr("&="); - case 'S': - First += 2; - return getDerived().parseBinaryExpr("="); - case 't': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return nullptr; - return make("alignof (", Ty, ")"); - } - case 'z': { - First += 2; - Node *Ty = getDerived().parseExpr(); - if (Ty == nullptr) - return nullptr; - return make("alignof (", Ty, ")"); - } - } - return nullptr; - case 'c': - switch (First[1]) { - // cc # const_cast(expression) - case 'c': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return Ty; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make("const_cast", Ty, Ex); - } - // cl + E # call - case 'l': { - First += 2; - Node *Callee = getDerived().parseExpr(); - if (Callee == nullptr) - return Callee; - size_t ExprsBegin = Names.size(); - while (!consumeIf('E')) { - Node *E = getDerived().parseExpr(); - if (E == nullptr) - return E; - Names.push_back(E); - } - return make(Callee, popTrailingNodeArray(ExprsBegin)); - } - case 'm': - First += 2; - return getDerived().parseBinaryExpr(","); - case 'o': - First += 2; - return getDerived().parsePrefixExpr("~"); - case 'v': - return getDerived().parseConversionExpr(); - } - return nullptr; - case 'd': - switch (First[1]) { - case 'a': { - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make(Ex, Global, /*is_array=*/true); - } - case 'c': { - First += 2; - Node *T = getDerived().parseType(); - if (T == nullptr) - return T; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make("dynamic_cast", T, Ex); - } - case 'e': - First += 2; - return getDerived().parsePrefixExpr("*"); - case 'l': { - First += 2; - Node *E = getDerived().parseExpr(); + if (consumeIf("il")) { + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseBracedExpr(); if (E == nullptr) - return E; - return make(E, Global, /*is_array=*/false); - } - case 'n': - return getDerived().parseUnresolvedName(); - case 's': { - First += 2; - Node *LHS = getDerived().parseExpr(); - if (LHS == nullptr) - return nullptr; - Node *RHS = getDerived().parseExpr(); - if (RHS == nullptr) - return nullptr; - return make(LHS, ".*", RHS); - } - case 't': { - First += 2; - Node *LHS = getDerived().parseExpr(); - if (LHS == nullptr) - return LHS; - Node *RHS = getDerived().parseExpr(); - if (RHS == nullptr) - return nullptr; - return make(LHS, ".", RHS); - } - case 'v': - First += 2; - return getDerived().parseBinaryExpr("/"); - case 'V': - First += 2; - return getDerived().parseBinaryExpr("/="); - } - return nullptr; - case 'e': - switch (First[1]) { - case 'o': - First += 2; - return getDerived().parseBinaryExpr("^"); - case 'O': - First += 2; - return getDerived().parseBinaryExpr("^="); - case 'q': - First += 2; - return getDerived().parseBinaryExpr("=="); - } - return nullptr; - case 'g': - switch (First[1]) { - case 'e': - First += 2; - return getDerived().parseBinaryExpr(">="); - case 't': - First += 2; - return getDerived().parseBinaryExpr(">"); - } - return nullptr; - case 'i': - switch (First[1]) { - case 'x': { - First += 2; - Node *Base = getDerived().parseExpr(); - if (Base == nullptr) - return nullptr; - Node *Index = getDerived().parseExpr(); - if (Index == nullptr) - return Index; - return make(Base, Index); - } - case 'l': { - First += 2; - size_t InitsBegin = Names.size(); - while (!consumeIf('E')) { - Node *E = getDerived().parseBracedExpr(); - if (E == nullptr) - return nullptr; - Names.push_back(E); - } - return make(nullptr, popTrailingNodeArray(InitsBegin)); - } - } - return nullptr; - case 'l': - switch (First[1]) { - case 'e': - First += 2; - return getDerived().parseBinaryExpr("<="); - case 's': - First += 2; - return getDerived().parseBinaryExpr("<<"); - case 'S': - First += 2; - return getDerived().parseBinaryExpr("<<="); - case 't': - First += 2; - return getDerived().parseBinaryExpr("<"); - } - return nullptr; - case 'm': - switch (First[1]) { - case 'c': - First += 2; - return parsePointerToMemberConversionExpr(); - case 'i': - First += 2; - return getDerived().parseBinaryExpr("-"); - case 'I': - First += 2; - return getDerived().parseBinaryExpr("-="); - case 'l': - First += 2; - return getDerived().parseBinaryExpr("*"); - case 'L': - First += 2; - return getDerived().parseBinaryExpr("*="); - case 'm': - First += 2; - if (consumeIf('_')) - return getDerived().parsePrefixExpr("--"); - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return nullptr; - return make(Ex, "--"); - } - return nullptr; - case 'n': - switch (First[1]) { - case 'a': - case 'w': - return getDerived().parseNewExpr(); - case 'e': - First += 2; - return getDerived().parseBinaryExpr("!="); - case 'g': - First += 2; - return getDerived().parsePrefixExpr("-"); - case 't': - First += 2; - return getDerived().parsePrefixExpr("!"); - case 'x': - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make("noexcept (", Ex, ")"); - } - return nullptr; - case 'o': - switch (First[1]) { - case 'n': - return getDerived().parseUnresolvedName(); - case 'o': - First += 2; - return getDerived().parseBinaryExpr("||"); - case 'r': - First += 2; - return getDerived().parseBinaryExpr("|"); - case 'R': - First += 2; - return getDerived().parseBinaryExpr("|="); - } - return nullptr; - case 'p': - switch (First[1]) { - case 'm': - First += 2; - return getDerived().parseBinaryExpr("->*"); - case 'l': - First += 2; - return getDerived().parseBinaryExpr("+"); - case 'L': - First += 2; - return getDerived().parseBinaryExpr("+="); - case 'p': { - First += 2; - if (consumeIf('_')) - return getDerived().parsePrefixExpr("++"); - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make(Ex, "++"); - } - case 's': - First += 2; - return getDerived().parsePrefixExpr("+"); - case 't': { - First += 2; - Node *L = getDerived().parseExpr(); - if (L == nullptr) return nullptr; - Node *R = getDerived().parseExpr(); + Names.push_back(E); + } + return make(nullptr, popTrailingNodeArray(InitsBegin)); + } + if (consumeIf("mc")) + return parsePointerToMemberConversionExpr(Node::Prec::Unary); + if (consumeIf("nx")) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return Ex; + return make("noexcept ", Ex, Node::Prec::Unary); + } + if (consumeIf("so")) + return parseSubobjectExpr(); + if (consumeIf("sp")) { + Node *Child = getDerived().parseExpr(); + if (Child == nullptr) + return nullptr; + return make(Child); + } + if (consumeIf("sZ")) { + if (look() == 'T') { + Node *R = getDerived().parseTemplateParam(); if (R == nullptr) return nullptr; - return make(L, "->", R); + return make(R); } - } - return nullptr; - case 'q': - if (First[1] == 'u') { - First += 2; - Node *Cond = getDerived().parseExpr(); - if (Cond == nullptr) - return nullptr; - Node *LHS = getDerived().parseExpr(); - if (LHS == nullptr) - return nullptr; - Node *RHS = getDerived().parseExpr(); - if (RHS == nullptr) - return nullptr; - return make(Cond, LHS, RHS); - } - return nullptr; - case 'r': - switch (First[1]) { - case 'c': { - First += 2; - Node *T = getDerived().parseType(); - if (T == nullptr) - return T; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make("reinterpret_cast", T, Ex); - } - case 'm': - First += 2; - return getDerived().parseBinaryExpr("%"); - case 'M': - First += 2; - return getDerived().parseBinaryExpr("%="); - case 's': - First += 2; - return getDerived().parseBinaryExpr(">>"); - case 'S': - First += 2; - return getDerived().parseBinaryExpr(">>="); - } - return nullptr; - case 's': - switch (First[1]) { - case 'c': { - First += 2; - Node *T = getDerived().parseType(); - if (T == nullptr) - return T; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make("static_cast", T, Ex); - } - case 'o': - First += 2; - return parseSubobjectExpr(); - case 'p': { - First += 2; - Node *Child = getDerived().parseExpr(); - if (Child == nullptr) - return nullptr; - return make(Child); - } - case 'r': - return getDerived().parseUnresolvedName(); - case 't': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return Ty; - return make("sizeof (", Ty, ")"); - } - case 'z': { - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make("sizeof (", Ex, ")"); - } - case 'Z': - First += 2; - if (look() == 'T') { - Node *R = getDerived().parseTemplateParam(); - if (R == nullptr) - return nullptr; - return make(R); - } else if (look() == 'f') { - Node *FP = getDerived().parseFunctionParam(); - if (FP == nullptr) - return nullptr; - return make("sizeof... (", FP, ")"); - } + Node *FP = getDerived().parseFunctionParam(); + if (FP == nullptr) return nullptr; - case 'P': { - First += 2; - size_t ArgsBegin = Names.size(); - while (!consumeIf('E')) { - Node *Arg = getDerived().parseTemplateArg(); - if (Arg == nullptr) - return nullptr; - Names.push_back(Arg); - } - auto *Pack = make(popTrailingNodeArray(ArgsBegin)); - if (!Pack) - return nullptr; - return make("sizeof... (", Pack, ")"); - } - } - return nullptr; - case 't': - switch (First[1]) { - case 'e': { - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make("typeid (", Ex, ")"); - } - case 'i': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return Ty; - return make("typeid (", Ty, ")"); - } - case 'l': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) + return make("sizeof... ", FP); + } + if (consumeIf("sP")) { + size_t ArgsBegin = Names.size(); + while (!consumeIf('E')) { + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) return nullptr; - size_t InitsBegin = Names.size(); - while (!consumeIf('E')) { - Node *E = getDerived().parseBracedExpr(); - if (E == nullptr) - return nullptr; - Names.push_back(E); - } - return make(Ty, popTrailingNodeArray(InitsBegin)); + Names.push_back(Arg); } - case 'r': - First += 2; - return make("throw"); - case 'w': { - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) + auto *Pack = make(popTrailingNodeArray(ArgsBegin)); + if (!Pack) + return nullptr; + return make("sizeof... ", Pack); + } + if (consumeIf("tl")) { + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseBracedExpr(); + if (E == nullptr) return nullptr; - return make(Ex); - } - } - return nullptr; - case 'u': { - ++First; + Names.push_back(E); + } + return make(Ty, popTrailingNodeArray(InitsBegin)); + } + if (consumeIf("tr")) + return make("throw"); + if (consumeIf("tw")) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make(Ex); + } + if (consumeIf('u')) { Node *Name = getDerived().parseSourceName(/*NameState=*/nullptr); if (!Name) return nullptr; @@ -5060,45 +4776,36 @@ Node *AbstractManglingParser::parseExpr() { // interpreted as node 'short' or 'ellipsis'. However, neither // __uuidof(short) nor __uuidof(...) can actually appear, so there is no // actual conflict here. + bool IsUUID = false; + Node *UUID = nullptr; if (Name->getBaseName() == "__uuidof") { - if (numLeft() < 2) - return nullptr; - if (*First == 't') { - ++First; - Node *Ty = getDerived().parseType(); - if (!Ty) - return nullptr; - return make(Name, makeNodeArray(&Ty, &Ty + 1)); - } - if (*First == 'z') { - ++First; - Node *Ex = getDerived().parseExpr(); - if (!Ex) - return nullptr; - return make(Name, makeNodeArray(&Ex, &Ex + 1)); + if (consumeIf('t')) { + UUID = getDerived().parseType(); + IsUUID = true; + } else if (consumeIf('z')) { + UUID = getDerived().parseExpr(); + IsUUID = true; } } size_t ExprsBegin = Names.size(); - while (!consumeIf('E')) { - Node *E = getDerived().parseTemplateArg(); - if (E == nullptr) - return E; - Names.push_back(E); + if (IsUUID) { + if (UUID == nullptr) + return nullptr; + Names.push_back(UUID); + } else { + while (!consumeIf('E')) { + Node *E = getDerived().parseTemplateArg(); + if (E == nullptr) + return E; + Names.push_back(E); + } } - return make(Name, popTrailingNodeArray(ExprsBegin)); + return make(Name, popTrailingNodeArray(ExprsBegin), + Node::Prec::Postfix); } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return getDerived().parseUnresolvedName(); - } - return nullptr; + + // Only unresolved names remain. + return getDerived().parseUnresolvedName(Global); } // ::= h _ @@ -5131,14 +4838,17 @@ bool AbstractManglingParser::parseCallOffset() { // # second call-offset is result adjustment // ::= T // # base is the nominal target function of thunk -// ::= GV # Guard variable for one-time initialization +// # Guard variable for one-time initialization +// ::= GV // # No // ::= TW # Thread-local wrapper // ::= TH # Thread-local initialization // ::= GR _ # First temporary // ::= GR _ # Subsequent temporaries -// extension ::= TC _ # construction vtable for second-in-first +// # construction vtable for second-in-first +// extension ::= TC _ // extension ::= GR # reference temporary for object +// extension ::= GI # module global initializer template Node *AbstractManglingParser::parseSpecialName() { switch (look()) { @@ -5265,6 +4975,16 @@ Node *AbstractManglingParser::parseSpecialName() { return nullptr; return make("reference temporary for ", Name); } + // GI v + case 'I': { + First += 2; + ModuleName *Module = nullptr; + if (getDerived().parseModuleNameOpt(Module)) + return nullptr; + if (Module == nullptr) + return nullptr; + return make("initializer for module ", Module); + } } } return nullptr; @@ -5379,7 +5099,7 @@ template <> struct FloatData { #if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ - defined(__wasm__) + defined(__wasm__) || defined(__riscv) static const size_t mangled_size = 32; #elif defined(__arm__) || defined(__mips__) || defined(__hexagon__) static const size_t mangled_size = 16; @@ -5444,6 +5164,7 @@ bool AbstractManglingParser::parseSeqId(size_t *Out) { // ::= Si # ::std::basic_istream > // ::= So # ::std::basic_ostream > // ::= Sd # ::std::basic_iostream > +// The St case is handled specially in parseNestedName. template Node *AbstractManglingParser::parseSubstitution() { if (!consumeIf('S')) diff --git a/lib/libcxxabi/src/demangle/ItaniumNodes.def b/lib/libcxxabi/src/demangle/ItaniumNodes.def new file mode 100644 index 0000000000000000000000000000000000000000..f615cb9fadb05e832eea1b8a1cb04ba240efe9ed --- /dev/null +++ b/lib/libcxxabi/src/demangle/ItaniumNodes.def @@ -0,0 +1,95 @@ +//===------------------------- ItaniumNodes.def ----------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Define the demangler's node names + +#ifndef NODE +#error Define NODE to handle nodes +#endif + +NODE(NodeArrayNode) +NODE(DotSuffix) +NODE(VendorExtQualType) +NODE(QualType) +NODE(ConversionOperatorType) +NODE(PostfixQualifiedType) +NODE(ElaboratedTypeSpefType) +NODE(NameType) +NODE(AbiTagAttr) +NODE(EnableIfAttr) +NODE(ObjCProtoName) +NODE(PointerType) +NODE(ReferenceType) +NODE(PointerToMemberType) +NODE(ArrayType) +NODE(FunctionType) +NODE(NoexceptSpec) +NODE(DynamicExceptionSpec) +NODE(FunctionEncoding) +NODE(LiteralOperator) +NODE(SpecialName) +NODE(CtorVtableSpecialName) +NODE(QualifiedName) +NODE(NestedName) +NODE(LocalName) +NODE(ModuleName) +NODE(ModuleEntity) +NODE(VectorType) +NODE(PixelVectorType) +NODE(BinaryFPType) +NODE(BitIntType) +NODE(SyntheticTemplateParamName) +NODE(TypeTemplateParamDecl) +NODE(NonTypeTemplateParamDecl) +NODE(TemplateTemplateParamDecl) +NODE(TemplateParamPackDecl) +NODE(ParameterPack) +NODE(TemplateArgumentPack) +NODE(ParameterPackExpansion) +NODE(TemplateArgs) +NODE(ForwardTemplateReference) +NODE(NameWithTemplateArgs) +NODE(GlobalQualifiedName) +NODE(ExpandedSpecialSubstitution) +NODE(SpecialSubstitution) +NODE(CtorDtorName) +NODE(DtorName) +NODE(UnnamedTypeName) +NODE(ClosureTypeName) +NODE(StructuredBindingName) +NODE(BinaryExpr) +NODE(ArraySubscriptExpr) +NODE(PostfixExpr) +NODE(ConditionalExpr) +NODE(MemberExpr) +NODE(SubobjectExpr) +NODE(EnclosingExpr) +NODE(CastExpr) +NODE(SizeofParamPackExpr) +NODE(CallExpr) +NODE(NewExpr) +NODE(DeleteExpr) +NODE(PrefixExpr) +NODE(FunctionParam) +NODE(ConversionExpr) +NODE(PointerToMemberConversionExpr) +NODE(InitListExpr) +NODE(FoldExpr) +NODE(ThrowExpr) +NODE(BoolExpr) +NODE(StringLiteral) +NODE(LambdaExpr) +NODE(EnumLiteral) +NODE(IntegerLiteral) +NODE(FloatLiteral) +NODE(DoubleLiteral) +NODE(LongDoubleLiteral) +NODE(BracedExpr) +NODE(BracedRangeExpr) + +#undef NODE diff --git a/lib/libcxxabi/src/demangle/Utility.h b/lib/libcxxabi/src/demangle/Utility.h index 3b27328522a6f0156d93bdda32d5b7bd7af19340..db19dcac0147d89610a527232a99fa40cf05eb8b 100644 --- a/lib/libcxxabi/src/demangle/Utility.h +++ b/lib/libcxxabi/src/demangle/Utility.h @@ -33,43 +33,50 @@ class OutputBuffer { size_t CurrentPosition = 0; size_t BufferCapacity = 0; - // Ensure there is at least n more positions in buffer. + // Ensure there are at least N more positions in the buffer. void grow(size_t N) { - if (N + CurrentPosition >= BufferCapacity) { + size_t Need = N + CurrentPosition; + if (Need > BufferCapacity) { + // Reduce the number of reallocations, with a bit of hysteresis. The + // number here is chosen so the first allocation will more-than-likely not + // allocate more than 1K. + Need += 1024 - 32; BufferCapacity *= 2; - if (BufferCapacity < N + CurrentPosition) - BufferCapacity = N + CurrentPosition; + if (BufferCapacity < Need) + BufferCapacity = Need; Buffer = static_cast(std::realloc(Buffer, BufferCapacity)); if (Buffer == nullptr) std::terminate(); } } - void writeUnsigned(uint64_t N, bool isNeg = false) { - // Handle special case... - if (N == 0) { - *this << '0'; - return; - } - + OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) { std::array Temp; char *TempPtr = Temp.data() + Temp.size(); - while (N) { + // Output at least one character. + do { *--TempPtr = char('0' + N % 10); N /= 10; - } + } while (N); - // Add negative sign... + // Add negative sign. if (isNeg) *--TempPtr = '-'; - this->operator<<(StringView(TempPtr, Temp.data() + Temp.size())); + + return operator+=(StringView(TempPtr, Temp.data() + Temp.size())); } public: OutputBuffer(char *StartBuf, size_t Size) : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} OutputBuffer() = default; + // Non-copyable + OutputBuffer(const OutputBuffer &) = delete; + OutputBuffer &operator=(const OutputBuffer &) = delete; + + operator StringView() const { return StringView(Buffer, CurrentPosition); } + void reset(char *Buffer_, size_t BufferCapacity_) { CurrentPosition = 0; Buffer = Buffer_; @@ -81,13 +88,27 @@ public: unsigned CurrentPackIndex = std::numeric_limits::max(); unsigned CurrentPackMax = std::numeric_limits::max(); + /// When zero, we're printing template args and '>' needs to be parenthesized. + /// Use a counter so we can simply increment inside parentheses. + unsigned GtIsGt = 1; + + bool isGtInsideTemplateArgs() const { return GtIsGt == 0; } + + void printOpen(char Open = '(') { + GtIsGt++; + *this += Open; + } + void printClose(char Close = ')') { + GtIsGt--; + *this += Close; + } + OutputBuffer &operator+=(StringView R) { - size_t Size = R.size(); - if (Size == 0) - return *this; - grow(Size); - std::memmove(Buffer + CurrentPosition, R.begin(), Size); - CurrentPosition += Size; + if (size_t Size = R.size()) { + grow(Size); + std::memcpy(Buffer + CurrentPosition, R.begin(), Size); + CurrentPosition += Size; + } return *this; } @@ -97,9 +118,7 @@ public: return *this; } - OutputBuffer &operator<<(StringView R) { return (*this += R); } - - OutputBuffer prepend(StringView R) { + OutputBuffer &prepend(StringView R) { size_t Size = R.size(); grow(Size); @@ -110,19 +129,16 @@ public: return *this; } + OutputBuffer &operator<<(StringView R) { return (*this += R); } + OutputBuffer &operator<<(char C) { return (*this += C); } OutputBuffer &operator<<(long long N) { - if (N < 0) - writeUnsigned(static_cast(-N), true); - else - writeUnsigned(static_cast(N)); - return *this; + return writeUnsigned(static_cast(std::abs(N)), N < 0); } OutputBuffer &operator<<(unsigned long long N) { - writeUnsigned(N, false); - return *this; + return writeUnsigned(N, false); } OutputBuffer &operator<<(long N) { @@ -155,7 +171,8 @@ public: void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } char back() const { - return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0'; + assert(CurrentPosition); + return Buffer[CurrentPosition - 1]; } bool empty() const { return CurrentPosition == 0; } @@ -165,35 +182,20 @@ public: size_t getBufferCapacity() const { return BufferCapacity; } }; -template class SwapAndRestore { - T &Restore; - T OriginalValue; - bool ShouldRestore = true; +template class ScopedOverride { + T &Loc; + T Original; public: - SwapAndRestore(T &Restore_) : SwapAndRestore(Restore_, Restore_) {} + ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {} - SwapAndRestore(T &Restore_, T NewVal) - : Restore(Restore_), OriginalValue(Restore) { - Restore = std::move(NewVal); - } - ~SwapAndRestore() { - if (ShouldRestore) - Restore = std::move(OriginalValue); - } - - void shouldRestore(bool ShouldRestore_) { ShouldRestore = ShouldRestore_; } - - void restoreNow(bool Force) { - if (!Force && !ShouldRestore) - return; - - Restore = std::move(OriginalValue); - ShouldRestore = false; + ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) { + Loc_ = std::move(NewVal); } + ~ScopedOverride() { Loc = std::move(Original); } - SwapAndRestore(const SwapAndRestore &) = delete; - SwapAndRestore &operator=(const SwapAndRestore &) = delete; + ScopedOverride(const ScopedOverride &) = delete; + ScopedOverride &operator=(const ScopedOverride &) = delete; }; inline bool initializeOutputBuffer(char *Buf, size_t *N, OutputBuffer &OB, diff --git a/lib/libcxxabi/src/fallback_malloc.cpp b/lib/libcxxabi/src/fallback_malloc.cpp index 7e356d9fe47ba20716b70816ee80ea8c72a1c14b..1d6c3808b2174ca1a8a18d9fc7911a5b8269ba1b 100644 --- a/lib/libcxxabi/src/fallback_malloc.cpp +++ b/lib/libcxxabi/src/fallback_malloc.cpp @@ -33,10 +33,9 @@ namespace { // When POSIX threads are not available, make the mutex operations a nop #ifndef _LIBCXXABI_HAS_NO_THREADS -_LIBCPP_SAFE_STATIC -static std::__libcpp_mutex_t heap_mutex = _LIBCPP_MUTEX_INITIALIZER; +static _LIBCPP_CONSTINIT std::__libcpp_mutex_t heap_mutex = _LIBCPP_MUTEX_INITIALIZER; #else -static void* heap_mutex = 0; +static _LIBCPP_CONSTINIT void* heap_mutex = 0; #endif class mutexor { diff --git a/src/libcxx.zig b/src/libcxx.zig index fdb9dc543c8a72110c511cc72e74f31d796b285b..301652fe6ba48ad8b014721c77d67f537e1f92e9 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -327,7 +327,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { try cflags.append("-nostdinc++"); try cflags.append("-fstrict-aliasing"); try cflags.append("-funwind-tables"); - try cflags.append("-std=c++11"); + try cflags.append("-std=c++20"); c_source_files.appendAssumeCapacity(.{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }),