[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