[libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of `exception_ptr` for Windows (PR #94977)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jun 11 20:21:44 PDT 2024
================
@@ -0,0 +1,541 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// This implementation communicates with the EH runtime though vcruntime's per-thread-data structure; see
+// _pCurrentException in <trnsctrl.h>.
+//
+// As a result, normal EH runtime services (such as noexcept functions) are safe to use in this file.
+
+#ifndef _VCRT_ALLOW_INTERNALS
+#define _VCRT_ALLOW_INTERNALS
+#endif // !defined(_VCRT_ALLOW_INTERNALS)
+
+#include <Unknwn.h>
+#include <cstdlib> // for abort
+#include <cstring> // for memcpy
+#include <eh.h>
+#include <ehdata.h>
+#include <exception>
+#include <internal_shared.h>
+#include <malloc.h>
+#include <memory>
+#include <new>
+#include <stdexcept>
+#include <trnsctrl.h>
+#include <xcall_once.h>
+
+#include <Windows.h>
+
+// Pre-V4 managed exception code
+#define MANAGED_EXCEPTION_CODE 0XE0434F4D
+
+// V4 and later managed exception code
+#define MANAGED_EXCEPTION_CODE_V4 0XE0434352
+
+extern "C" _CRTIMP2 void* __cdecl __AdjustPointer(void*, const PMD&); // defined in frame.cpp
+
+using namespace std;
+
+namespace {
+#if defined(_M_CEE_PURE)
+ template <class _Ty>
+ _Ty& _Immortalize() { // return a reference to an object that will live forever
+ /* MAGIC */ static _Immortalizer_impl<_Ty> _Static;
+ return reinterpret_cast<_Ty&>(_Static._Storage);
+ }
+#elif !defined(_M_CEE)
+ template <class _Ty>
+ struct _Constexpr_excptptr_immortalize_impl {
+ union {
+ _Ty _Storage;
+ };
+
+ constexpr _Constexpr_excptptr_immortalize_impl() noexcept : _Storage{} {}
+
+ _Constexpr_excptptr_immortalize_impl(const _Constexpr_excptptr_immortalize_impl&) = delete;
+ _Constexpr_excptptr_immortalize_impl& operator=(const _Constexpr_excptptr_immortalize_impl&) = delete;
+
+ _MSVC_NOOP_DTOR ~_Constexpr_excptptr_immortalize_impl() {
+ // do nothing, allowing _Ty to be used during shutdown
+ }
+ };
+
+ template <class _Ty>
+ _Constexpr_excptptr_immortalize_impl<_Ty> _Immortalize_impl;
+
+ template <class _Ty>
+ [[nodiscard]] _Ty& _Immortalize() noexcept {
+ return _Immortalize_impl<_Ty>._Storage;
+ }
+#else // ^^^ !defined(_M_CEE) / defined(_M_CEE), TRANSITION, VSO-1153256 vvv
+ template <class _Ty>
+ _Ty& _Immortalize() { // return a reference to an object that will live forever
+ static once_flag _Flag;
+ alignas(_Ty) static unsigned char _Storage[sizeof(_Ty)];
+ call_once(_Flag, [&_Storage] { ::new (static_cast<void*>(&_Storage)) _Ty(); });
+ return reinterpret_cast<_Ty&>(_Storage);
+ }
+#endif // ^^^ !defined(_M_CEE_PURE) && defined(_M_CEE), TRANSITION, VSO-1153256 ^^^
+
+ void _PopulateCppExceptionRecord(
+ _EXCEPTION_RECORD& _Record, const void* const _PExcept, ThrowInfo* _PThrow) noexcept {
+ _Record.ExceptionCode = EH_EXCEPTION_NUMBER;
+ _Record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ _Record.ExceptionRecord = nullptr; // no SEH to chain
+ _Record.ExceptionAddress = nullptr; // Address of exception. Will be overwritten by OS
+ _Record.NumberParameters = EH_EXCEPTION_PARAMETERS;
+ _Record.ExceptionInformation[0] = EH_MAGIC_NUMBER1; // params.magicNumber
+ _Record.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(_PExcept); // params.pExceptionObject
+
+ if (_PThrow && (_PThrow->attributes & TI_IsWinRT)) {
+ // The pointer to the ExceptionInfo structure is stored sizeof(void*) in front of each WinRT Exception Info.
+ const auto _PWei = (*static_cast<WINRTEXCEPTIONINFO** const*>(_PExcept))[-1];
+ _PThrow = _PWei->throwInfo;
+ }
+
+ _Record.ExceptionInformation[2] = reinterpret_cast<ULONG_PTR>(_PThrow); // params.pThrowInfo
+
+#if _EH_RELATIVE_TYPEINFO
+ void* _ThrowImageBase =
+ _PThrow ? RtlPcToFileHeader(const_cast<void*>(static_cast<const void*>(_PThrow)), &_ThrowImageBase)
+ : nullptr;
+ _Record.ExceptionInformation[3] = reinterpret_cast<ULONG_PTR>(_ThrowImageBase); // params.pThrowImageBase
+#endif // _EH_RELATIVE_TYPEINFO
+
+ // If the throw info indicates this throw is from a pure region,
+ // set the magic number to the Pure one, so only a pure-region
+ // catch will see it.
+ //
+ // Also use the Pure magic number on 64-bit platforms if we were unable to
+ // determine an image base, since that was the old way to determine
+ // a pure throw, before the TI_IsPure bit was added to the FuncInfo
+ // attributes field.
+ if (_PThrow
+ && ((_PThrow->attributes & TI_IsPure)
+#if _EH_RELATIVE_TYPEINFO
+ || !_ThrowImageBase
+#endif // _EH_RELATIVE_TYPEINFO
+ )) {
+ _Record.ExceptionInformation[0] = EH_PURE_MAGIC_NUMBER1;
+ }
+ }
+
+ void _CopyExceptionRecord(_EXCEPTION_RECORD& _Dest, const _EXCEPTION_RECORD& _Src) noexcept {
+ _Dest.ExceptionCode = _Src.ExceptionCode;
+ // we force EXCEPTION_NONCONTINUABLE because rethrow_exception is [[noreturn]]
+ _Dest.ExceptionFlags = _Src.ExceptionFlags | EXCEPTION_NONCONTINUABLE;
+ _Dest.ExceptionRecord = nullptr; // We don't chain SEH exceptions
+ _Dest.ExceptionAddress = nullptr; // Useless field to copy. It will be overwritten by RaiseException()
+ const auto _Parameters = _Src.NumberParameters;
+ _Dest.NumberParameters = _Parameters;
+
+ // copy the number of parameters in use
+ constexpr auto _Max_parameters = static_cast<DWORD>(EXCEPTION_MAXIMUM_PARAMETERS);
+ const auto _In_use = (_STD min)(_Parameters, _Max_parameters);
+ _CSTD memcpy(_Dest.ExceptionInformation, _Src.ExceptionInformation, _In_use * sizeof(ULONG_PTR));
+ _CSTD memset(&_Dest.ExceptionInformation[_In_use], 0, (_Max_parameters - _In_use) * sizeof(ULONG_PTR));
+ }
+
+ void _CopyExceptionObject(void* _Dest, const void* _Src, const CatchableType* const _PType
+#if _EH_RELATIVE_TYPEINFO
+ ,
+ const uintptr_t _ThrowImageBase
+#endif // _EH_RELATIVE_TYPEINFO
+ ) {
+ // copy an object of type denoted by *_PType from _Src to _Dest; throws whatever the copy ctor of the type
+ // denoted by *_PType throws
+ if ((_PType->properties & CT_IsSimpleType) || _PType->copyFunction == 0) {
+ memcpy(_Dest, _Src, _PType->sizeOrOffset);
+
+ if (_PType->properties & CT_IsWinRTHandle) {
+ const auto _PUnknown = *static_cast<IUnknown* const*>(_Src);
+ if (_PUnknown) {
+ _PUnknown->AddRef();
+ }
+ }
+ return;
+ }
+
+#if _EH_RELATIVE_TYPEINFO
+ const auto _CopyFunc = reinterpret_cast<void*>(_ThrowImageBase + _PType->copyFunction);
+#else // ^^^ _EH_RELATIVE_TYPEINFO / !_EH_RELATIVE_TYPEINFO vvv
+ const auto _CopyFunc = _PType->copyFunction;
+#endif // ^^^ !_EH_RELATIVE_TYPEINFO ^^^
+
+ const auto _Adjusted = __AdjustPointer(const_cast<void*>(_Src), _PType->thisDisplacement);
+ if (_PType->properties & CT_HasVirtualBase) {
+#ifdef _M_CEE_PURE
+ reinterpret_cast<void(__clrcall*)(void*, void*, int)>(_CopyFunc)(_Dest, _Adjusted, 1);
+#else // ^^^ defined(_M_CEE_PURE) / !defined(_M_CEE_PURE) vvv
+ _CallMemberFunction2(_Dest, _CopyFunc, _Adjusted, 1);
+#endif // ^^^ !defined(_M_CEE_PURE) ^^^
+ } else {
+#ifdef _M_CEE_PURE
+ reinterpret_cast<void(__clrcall*)(void*, void*)>(_CopyFunc)(_Dest, _Adjusted);
+#else // ^^^ defined(_M_CEE_PURE) / !defined(_M_CEE_PURE) vvv
+ _CallMemberFunction1(_Dest, _CopyFunc, _Adjusted);
+#endif // ^^^ !defined(_M_CEE_PURE) ^^^
+ }
+ }
+} // unnamed namespace
+
+// All exception_ptr implementations are out-of-line because <memory> depends on <exception>,
+// which means <exception> cannot include <memory> -- and shared_ptr is defined in <memory>.
+// To workaround this, we created a dummy class exception_ptr, which is structurally identical to shared_ptr.
+
+_STD_BEGIN
+struct _Exception_ptr_access {
+ template <class _Ty, class _Ty2>
+ static void _Set_ptr_rep(_Ptr_base<_Ty>& _This, _Ty2* _Px, _Ref_count_base* _Rx) noexcept {
+ _This._Ptr = _Px;
+ _This._Rep = _Rx;
+ }
+};
+_STD_END
+
+static_assert(sizeof(exception_ptr) == sizeof(shared_ptr<const _EXCEPTION_RECORD>)
+ && alignof(exception_ptr) == alignof(shared_ptr<const _EXCEPTION_RECORD>),
+ "std::exception_ptr and std::shared_ptr<const _EXCEPTION_RECORD> must have the same layout.");
+
+namespace {
+ template <class _StaticEx>
+ class _ExceptionPtr_static final : public _Ref_count_base {
+ // reference count control block for special "never allocates" exceptions like the bad_alloc or bad_exception
+ // exception_ptrs
+ private:
+ void _Destroy() noexcept override {
+ // intentionally does nothing
+ }
+
+ void _Delete_this() noexcept override {
+ // intentionally does nothing
+ }
+
+ public:
+ // constexpr, TRANSITION P1064
+ explicit _ExceptionPtr_static() noexcept : _Ref_count_base() {
+ _PopulateCppExceptionRecord(_ExRecord, &_Ex, static_cast<ThrowInfo*>(__GetExceptionInfo(_Ex)));
+ }
----------------
frederick-vs-ja wrote:
It seems that we can never make this ctor `constexpr` as `_PopulateCppExceptionRecord` needs to perform `reinterpret_cast` and `RtlPcToFileHeader` (even if `__GetExceptionInfo` gets `constexpr`-ized one day).
Should we drop this comment (also in MSVC STL)?
https://github.com/llvm/llvm-project/pull/94977
More information about the libcxx-commits
mailing list