From f5e529db619e903278f6dea4fbc35a14d0a270ef Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sat, 29 May 2021 11:21:57 +0300 Subject: [PATCH] shared_ptr.hpp: don't use fake objects This lifts the limitation for casting with abstract classes. Use new C++20 feature (constexpr allocator) to test viability. Add SamePtr concept to types.hpp --- rpcs3/stdafx.cpp | 49 +++++++++++ rpcs3/util/shared_ptr.hpp | 173 ++++++------------------------------- rpcs3/util/typeindices.hpp | 4 +- rpcs3/util/types.hpp | 53 ++++++++++++ 4 files changed, 130 insertions(+), 149 deletions(-) diff --git a/rpcs3/stdafx.cpp b/rpcs3/stdafx.cpp index c33f2e6646..490ba62c6e 100644 --- a/rpcs3/stdafx.cpp +++ b/rpcs3/stdafx.cpp @@ -11,3 +11,52 @@ static_assert(be_t(1) + be_t(2) + be_t(3) == 6); static_assert(le_t(1) + le_t(2) + le_t(3) == 6); static_assert(sizeof(nullptr) == sizeof(void*)); + +static_assert(__cpp_constexpr_dynamic_alloc >= 201907L); + +namespace +{ + struct A + { + int a; + }; + + struct B : A + { + int b; + }; + + struct Z + { + }; + + struct C + { + virtual ~C() = 0; + int C; + }; + + struct D : Z, B + { + int d; + }; + + struct E : C, B + { + int e; + }; + + struct F : C + { + virtual ~F() = 0; + }; + + static_assert(is_same_ptr()); + static_assert(is_same_ptr()); + static_assert(is_same_ptr()); + static_assert(is_same_ptr()); + static_assert(!is_same_ptr()); + static_assert(!is_same_ptr()); + static_assert(is_same_ptr()); + static_assert(is_same_ptr()); +} diff --git a/rpcs3/util/shared_ptr.hpp b/rpcs3/util/shared_ptr.hpp index ba2df2633f..1f6bdf26f4 100644 --- a/rpcs3/util/shared_ptr.hpp +++ b/rpcs3/util/shared_ptr.hpp @@ -6,63 +6,8 @@ namespace stx { - namespace detail - { - template - union fake_t - { - char dummy; - T data; - - fake_t() noexcept {} - ~fake_t() {} - }; - - template -#ifdef _MSC_VER - static const -#else - static thread_local const -#endif - fake_t> sample{}; - } - - // Classify compile-time information available for pointers - enum class same_ptr - { - no, - yes, - maybe - }; - - template - constexpr bool is_same_ptr_test(const volatile Y* ptr = std::addressof(detail::sample.data)) - { - return static_cast(ptr) == static_cast(ptr); - } - - // Checks whether the cast between two types is the same pointer - template - constexpr same_ptr is_same_ptr() noexcept - { - if constexpr (std::is_void_v || std::is_void_v || std::is_same_v, std::remove_cv_t>) - { - return same_ptr::yes; - } - else if constexpr (PtrCastable && !std::is_abstract_v) - { - return is_same_ptr_test() ? same_ptr::yes : same_ptr::no; - } - else if constexpr (PtrCastable && !std::is_abstract_v) - { - return is_same_ptr_test() ? same_ptr::yes : same_ptr::no; - } - - return same_ptr::maybe; - } - - template - constexpr same_ptr is_same_ptr_cast_v = std::is_convertible_v ? is_same_ptr() : same_ptr::no; + template + constexpr bool same_ptr_implicit_v = std::is_convertible_v ? is_same_ptr() : false; template class single_ptr; @@ -175,12 +120,9 @@ namespace stx r.m_ptr = nullptr; } - template != same_ptr::no>> + template requires same_ptr_implicit_v single_ptr(single_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - m_ptr = r.m_ptr; r.m_ptr = nullptr; } @@ -200,12 +142,9 @@ namespace stx return *this; } - template != same_ptr::no>> + template requires same_ptr_implicit_v single_ptr& operator=(single_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - single_ptr(std::move(r)).swap(*this); return *this; } @@ -269,12 +208,9 @@ namespace stx } // "Moving" "static cast" - template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> + template requires PtrSame explicit operator single_ptr() && noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(m_ptr)); - single_ptr r; r.m_ptr = static_cast(std::exchange(m_ptr, nullptr)); return r; @@ -436,12 +372,9 @@ namespace stx ensure((d()->refs++ - 1) >> 58 == 0); } - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_ptr(const shared_ptr& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - m_ptr = r.m_ptr; if (m_ptr) d()->refs++; @@ -453,22 +386,16 @@ namespace stx r.m_ptr = nullptr; } - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_ptr(shared_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - m_ptr = r.m_ptr; r.m_ptr = nullptr; } - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_ptr(single_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - m_ptr = r.m_ptr; r.m_ptr = nullptr; } @@ -486,12 +413,9 @@ namespace stx [[deprecated("Use null_ptr")]] shared_ptr& operator=(std::nullptr_t) = delete; - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_ptr& operator=(const shared_ptr& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - shared_ptr(r).swap(*this); return *this; } @@ -502,22 +426,16 @@ namespace stx return *this; } - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_ptr& operator=(shared_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - shared_ptr(std::move(r)).swap(*this); return *this; } - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_ptr& operator=(single_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - shared_ptr(std::move(r)).swap(*this); return *this; } @@ -535,12 +453,9 @@ namespace stx } // Converts to unique (single) ptr if reference is 1, otherwise returns null. Nullifies self. - template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> + template requires PtrSame explicit operator single_ptr() && noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(m_ptr)); - const auto o = d(); if (m_ptr && !--o->refs) @@ -618,12 +533,9 @@ namespace stx } // Basic "static cast" support - template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> + template requires PtrSame explicit operator shared_ptr() const& noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(m_ptr)); - if (m_ptr) { d()->refs++; @@ -635,12 +547,9 @@ namespace stx } // "Moving" "static cast" - template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> + template requires PtrSame explicit operator shared_ptr() && noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(m_ptr)); - shared_ptr r; r.m_ptr = static_cast(std::exchange(m_ptr, nullptr)); return r; @@ -702,24 +611,18 @@ namespace stx d()->refs.raw() += c_ref_mask; } - template != same_ptr::no>> + template requires same_ptr_implicit_v atomic_ptr(const shared_ptr& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - // Obtain a ref + as many refs as an atomic_ptr can additionally reference m_val = reinterpret_cast(r.m_ptr) << c_ref_size; if (m_val) d()->refs += c_ref_mask + 1; } - template != same_ptr::no>> + template requires same_ptr_implicit_v atomic_ptr(shared_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - m_val = reinterpret_cast(r.m_ptr) << c_ref_size; r.m_ptr = nullptr; @@ -727,12 +630,9 @@ namespace stx d()->refs += c_ref_mask; } - template != same_ptr::no>> + template requires same_ptr_implicit_v atomic_ptr(single_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - m_val = reinterpret_cast(r.m_ptr) << c_ref_size; r.m_ptr = nullptr; @@ -762,32 +662,23 @@ namespace stx return *this; } - template != same_ptr::no>> + template requires same_ptr_implicit_v atomic_ptr& operator=(const shared_ptr& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - store(r); return *this; } - template != same_ptr::no>> + template requires same_ptr_implicit_v atomic_ptr& operator=(shared_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - store(std::move(r)); return *this; } - template != same_ptr::no>> + template requires same_ptr_implicit_v atomic_ptr& operator=(single_ptr&& r) noexcept { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(r.m_ptr)); - store(std::move(r)); return *this; } @@ -1048,24 +939,18 @@ namespace stx } // Unoptimized - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_type compare_and_swap(const shared_ptr& cmp, shared_type exch) { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(cmp.m_ptr)); - shared_type old = cmp; static_cast(compare_exchange(old, std::move(exch))); return old; } // More lightweight than compare_exchange - template != same_ptr::no>> + template requires same_ptr_implicit_v bool compare_and_swap_test(const shared_ptr& cmp, shared_type exch) { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(cmp.m_ptr)); - const uptr _old = reinterpret_cast(cmp.m_ptr); const uptr _new = reinterpret_cast(exch.m_ptr); @@ -1102,24 +987,18 @@ namespace stx } // Unoptimized - template != same_ptr::no>> + template requires same_ptr_implicit_v shared_type compare_and_swap(const single_ptr& cmp, shared_type exch) { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(cmp.m_ptr)); - shared_type old = cmp; static_cast(compare_exchange(old, std::move(exch))); return old; } // Supplementary - template != same_ptr::no>> + template requires same_ptr_implicit_v bool compare_and_swap_test(const single_ptr& cmp, shared_type exch) { - if constexpr (is_same_ptr() == same_ptr::maybe) - ensure(is_same_ptr_test(cmp.m_ptr)); - return compare_and_swap_test(reinterpret_cast&>(cmp), std::move(exch)); } @@ -1169,13 +1048,13 @@ namespace stx return m_val != 0; } - template != same_ptr::no>> + template requires same_ptr_implicit_v bool is_equal(const shared_ptr& r) const noexcept { return observe() == r.get(); } - template != same_ptr::no>> + template requires same_ptr_implicit_v bool is_equal(const single_ptr& r) const noexcept { return observe() == r.get(); diff --git a/rpcs3/util/typeindices.hpp b/rpcs3/util/typeindices.hpp index 05c791fad0..7dcac5950b 100644 --- a/rpcs3/util/typeindices.hpp +++ b/rpcs3/util/typeindices.hpp @@ -250,7 +250,7 @@ namespace stx else { static_assert(sizeof(As) > 0); - static_assert(is_same_ptr() == same_ptr::yes); // TODO + static_assert(PtrSame); return type_counter::template dyn_type.index(); } } @@ -269,7 +269,7 @@ namespace stx ATTR_PURE inline const Info& typedata() noexcept { static_assert(sizeof(T) > 0 && sizeof(As) > 0); - static_assert(is_same_ptr() == same_ptr::yes); // TODO + static_assert(PtrSame); // TODO return type_counter::template dyn_type; } diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp index 6cdb159c04..24639e0208 100644 --- a/rpcs3/util/types.hpp +++ b/rpcs3/util/types.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include using std::chrono::steady_clock; @@ -1017,3 +1018,55 @@ concept PtrCastable = requires(const volatile X* x, const volatile Y* y) static_cast(x); static_cast(y); }; + +template requires PtrCastable +constexpr bool is_same_ptr() +{ + if constexpr (std::is_void_v || std::is_void_v || std::is_same_v, std::remove_cv_t>) + { + return true; + } + else if constexpr (sizeof(X) == sizeof(Y)) + { + return true; + } + else + { + if (std::is_constant_evaluated()) + { + bool result = false; + + if constexpr (sizeof(X) < sizeof(Y)) + { + std::allocator a{}; + Y* ptr = a.allocate(1); + result = static_cast(ptr) == static_cast(ptr); + a.deallocate(ptr, 1); + } + else + { + std::allocator a{}; + X* ptr = a.allocate(1); + result = static_cast(ptr) == static_cast(ptr); + a.deallocate(ptr, 1); + } + + return result; + } + else + { + std::aligned_union_t<0, X, Y> s; + Y* ptr = reinterpret_cast(&s); + return static_cast(ptr) == static_cast(ptr); + } + } +} + +template requires PtrCastable +constexpr bool is_same_ptr(const volatile Y* ptr) +{ + return static_cast(ptr) == static_cast(ptr); +} + +template +concept PtrSame = (is_same_ptr());