diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/bit_util.h | 7 | ||||
-rw-r--r-- | src/common/common_types.h | 10 | ||||
-rw-r--r-- | src/common/fs/file.h | 5 | ||||
-rw-r--r-- | src/common/fs/fs_util.cpp | 8 | ||||
-rw-r--r-- | src/common/fs/fs_util.h | 18 | ||||
-rw-r--r-- | src/common/host_memory.cpp | 4 | ||||
-rw-r--r-- | src/common/input.h | 2 | ||||
-rw-r--r-- | src/common/intrusive_red_black_tree.h | 391 | ||||
-rw-r--r-- | src/common/logging/backend.cpp | 18 | ||||
-rw-r--r-- | src/common/logging/filter.cpp | 1 | ||||
-rw-r--r-- | src/common/logging/types.h | 1 | ||||
-rw-r--r-- | src/common/page_table.cpp | 58 | ||||
-rw-r--r-- | src/common/page_table.h | 24 | ||||
-rw-r--r-- | src/common/settings.cpp | 2 | ||||
-rw-r--r-- | src/common/settings.h | 4 | ||||
-rw-r--r-- | src/common/telemetry.cpp | 60 | ||||
-rw-r--r-- | src/common/telemetry.h | 36 | ||||
-rw-r--r-- | src/common/tree.h | 625 | ||||
-rw-r--r-- | src/common/uuid.cpp | 208 | ||||
-rw-r--r-- | src/common/uuid.h | 166 | ||||
-rw-r--r-- | src/common/wall_clock.cpp | 16 | ||||
-rw-r--r-- | src/common/wall_clock.h | 8 | ||||
-rw-r--r-- | src/common/x64/cpu_detect.cpp | 124 | ||||
-rw-r--r-- | src/common/x64/cpu_detect.h | 79 | ||||
-rw-r--r-- | src/common/x64/native_clock.cpp | 6 |
25 files changed, 1161 insertions, 720 deletions
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index f50d3308a..f37538e06 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -57,4 +57,11 @@ requires std::is_integral_v<T> return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); } +template <size_t bit_index, typename T> +requires std::is_integral_v<T> +[[nodiscard]] constexpr bool Bit(const T value) { + static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); + return ((value >> bit_index) & T(1)) == T(1); +} + } // namespace Common diff --git a/src/common/common_types.h b/src/common/common_types.h index 4cec89fbd..99bffc460 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h @@ -46,13 +46,3 @@ using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space using u128 = std::array<std::uint64_t, 2>; static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); - -// An inheritable class to disallow the copy constructor and operator= functions -class NonCopyable { -protected: - constexpr NonCopyable() = default; - ~NonCopyable() = default; - - NonCopyable(const NonCopyable&) = delete; - NonCopyable& operator=(const NonCopyable&) = delete; -}; diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 2c4ab4332..a4f7944cd 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -188,9 +188,8 @@ public: #ifdef _WIN32 template <typename Path> - [[nodiscard]] void Open(const Path& path, FileAccessMode mode, - FileType type = FileType::BinaryFile, - FileShareFlag flag = FileShareFlag::ShareReadOnly) { + void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile, + FileShareFlag flag = FileShareFlag::ShareReadOnly) { using ValueType = typename Path::value_type; if constexpr (IsChar<ValueType>) { Open(ToU8String(path), mode, type, flag); diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index 9f8671982..0068112e6 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) { return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; } +std::u8string_view BufferToU8StringView(std::span<const u8> buffer) { + return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())}; +} + std::string ToUTF8String(std::u8string_view u8_string) { return std::string{u8_string.begin(), u8_string.end()}; } @@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) { return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; } +std::string_view BufferToUTF8StringView(std::span<const u8> buffer) { + return std::string_view{reinterpret_cast<const char*>(buffer.data())}; +} + std::string PathToUTF8String(const std::filesystem::path& path) { return ToUTF8String(path.u8string()); } diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h index 1ec82eb35..1620d38c9 100644 --- a/src/common/fs/fs_util.h +++ b/src/common/fs/fs_util.h @@ -38,6 +38,15 @@ concept IsChar = std::same_as<T, char>; [[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); /** + * Same as BufferToU8String, but returns a string view of the buffer. + * + * @param buffer Buffer of bytes + * + * @returns UTF-8 encoded std::u8string_view. + */ +[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer); + +/** * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. * * @param u8_string UTF-8 encoded u8string @@ -58,6 +67,15 @@ concept IsChar = std::same_as<T, char>; [[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); /** + * Same as BufferToUTF8String, but returns a string view of the buffer. + * + * @param buffer Buffer of bytes + * + * @returns UTF-8 encoded std::string_view. + */ +[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer); + +/** * Converts a filesystem path to a UTF-8 encoded std::string. * * @param path Filesystem path diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 28949fe5e..c465cfc14 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -327,8 +327,8 @@ private: bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); if (it != placeholders.end() && it->lower() == virtual_offset + length) { - const bool is_root = it == placeholders.begin() && virtual_offset == 0; - return is_root || std::prev(it)->upper() == virtual_offset; + return it == placeholders.begin() ? virtual_offset == 0 + : std::prev(it)->upper() == virtual_offset; } return false; } diff --git a/src/common/input.h b/src/common/input.h index f4f9eb30a..54fcb24b0 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -209,6 +209,8 @@ enum class ButtonNames { Triangle, Share, Options, + Home, + Touch, // Mouse buttons ButtonMouseWheel, diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h index 3173cc449..b296b639e 100644 --- a/src/common/intrusive_red_black_tree.h +++ b/src/common/intrusive_red_black_tree.h @@ -4,6 +4,8 @@ #pragma once +#include "common/alignment.h" +#include "common/common_funcs.h" #include "common/parent_of_member.h" #include "common/tree.h" @@ -15,32 +17,33 @@ class IntrusiveRedBlackTreeImpl; } +#pragma pack(push, 4) struct IntrusiveRedBlackTreeNode { + YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode); + public: - using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; + using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>; - constexpr IntrusiveRedBlackTreeNode() = default; +private: + RBEntry m_entry; - void SetEntry(const EntryType& new_entry) { - entry = new_entry; - } +public: + explicit IntrusiveRedBlackTreeNode() = default; - [[nodiscard]] EntryType& GetEntry() { - return entry; + [[nodiscard]] constexpr RBEntry& GetRBEntry() { + return m_entry; } - - [[nodiscard]] const EntryType& GetEntry() const { - return entry; + [[nodiscard]] constexpr const RBEntry& GetRBEntry() const { + return m_entry; } -private: - EntryType entry{}; - - friend class impl::IntrusiveRedBlackTreeImpl; - - template <class, class, class> - friend class IntrusiveRedBlackTree; + constexpr void SetRBEntry(const RBEntry& entry) { + m_entry = entry; + } }; +static_assert(sizeof(IntrusiveRedBlackTreeNode) == + 3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4)); +#pragma pack(pop) template <class T, class Traits, class Comparator> class IntrusiveRedBlackTree; @@ -48,12 +51,17 @@ class IntrusiveRedBlackTree; namespace impl { class IntrusiveRedBlackTreeImpl { + YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl); + private: template <class, class, class> friend class ::Common::IntrusiveRedBlackTree; - using RootType = RBHead<IntrusiveRedBlackTreeNode>; - RootType root; +private: + using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>; + +private: + RootType m_root; public: template <bool Const> @@ -81,149 +89,150 @@ public: IntrusiveRedBlackTreeImpl::reference>; private: - pointer node; + pointer m_node; public: - explicit Iterator(pointer n) : node(n) {} + constexpr explicit Iterator(pointer n) : m_node(n) {} - bool operator==(const Iterator& rhs) const { - return this->node == rhs.node; + constexpr bool operator==(const Iterator& rhs) const { + return m_node == rhs.m_node; } - bool operator!=(const Iterator& rhs) const { + constexpr bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } - pointer operator->() const { - return this->node; + constexpr pointer operator->() const { + return m_node; } - reference operator*() const { - return *this->node; + constexpr reference operator*() const { + return *m_node; } - Iterator& operator++() { - this->node = GetNext(this->node); + constexpr Iterator& operator++() { + m_node = GetNext(m_node); return *this; } - Iterator& operator--() { - this->node = GetPrev(this->node); + constexpr Iterator& operator--() { + m_node = GetPrev(m_node); return *this; } - Iterator operator++(int) { + constexpr Iterator operator++(int) { const Iterator it{*this}; ++(*this); return it; } - Iterator operator--(int) { + constexpr Iterator operator--(int) { const Iterator it{*this}; --(*this); return it; } - operator Iterator<true>() const { - return Iterator<true>(this->node); + constexpr operator Iterator<true>() const { + return Iterator<true>(m_node); } }; private: - // Define accessors using RB_* functions. - bool EmptyImpl() const { - return root.IsEmpty(); + constexpr bool EmptyImpl() const { + return m_root.IsEmpty(); } - IntrusiveRedBlackTreeNode* GetMinImpl() const { - return RB_MIN(const_cast<RootType*>(&root)); + constexpr IntrusiveRedBlackTreeNode* GetMinImpl() const { + return freebsd::RB_MIN(const_cast<RootType&>(m_root)); } - IntrusiveRedBlackTreeNode* GetMaxImpl() const { - return RB_MAX(const_cast<RootType*>(&root)); + constexpr IntrusiveRedBlackTreeNode* GetMaxImpl() const { + return freebsd::RB_MAX(const_cast<RootType&>(m_root)); } - IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { - return RB_REMOVE(&root, node); + constexpr IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { + return freebsd::RB_REMOVE(m_root, node); } public: - static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { - return RB_NEXT(node); + static constexpr IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { + return freebsd::RB_NEXT(node); } - static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { - return RB_PREV(node); + static constexpr IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { + return freebsd::RB_PREV(node); } - static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) { + static constexpr IntrusiveRedBlackTreeNode const* GetNext( + IntrusiveRedBlackTreeNode const* node) { return static_cast<const IntrusiveRedBlackTreeNode*>( GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); } - static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) { + static constexpr IntrusiveRedBlackTreeNode const* GetPrev( + IntrusiveRedBlackTreeNode const* node) { return static_cast<const IntrusiveRedBlackTreeNode*>( GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); } public: - constexpr IntrusiveRedBlackTreeImpl() {} + constexpr IntrusiveRedBlackTreeImpl() = default; // Iterator accessors. - iterator begin() { + constexpr iterator begin() { return iterator(this->GetMinImpl()); } - const_iterator begin() const { + constexpr const_iterator begin() const { return const_iterator(this->GetMinImpl()); } - iterator end() { + constexpr iterator end() { return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); } - const_iterator end() const { + constexpr const_iterator end() const { return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); } - const_iterator cbegin() const { + constexpr const_iterator cbegin() const { return this->begin(); } - const_iterator cend() const { + constexpr const_iterator cend() const { return this->end(); } - iterator iterator_to(reference ref) { - return iterator(&ref); + constexpr iterator iterator_to(reference ref) { + return iterator(std::addressof(ref)); } - const_iterator iterator_to(const_reference ref) const { - return const_iterator(&ref); + constexpr const_iterator iterator_to(const_reference ref) const { + return const_iterator(std::addressof(ref)); } // Content management. - bool empty() const { + constexpr bool empty() const { return this->EmptyImpl(); } - reference back() { + constexpr reference back() { return *this->GetMaxImpl(); } - const_reference back() const { + constexpr const_reference back() const { return *this->GetMaxImpl(); } - reference front() { + constexpr reference front() { return *this->GetMinImpl(); } - const_reference front() const { + constexpr const_reference front() const { return *this->GetMinImpl(); } - iterator erase(iterator it) { + constexpr iterator erase(iterator it) { auto cur = std::addressof(*it); auto next = GetNext(cur); this->RemoveImpl(cur); @@ -234,16 +243,16 @@ public: } // namespace impl template <typename T> -concept HasLightCompareType = requires { - { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>; +concept HasRedBlackKeyType = requires { + { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>; }; namespace impl { template <typename T, typename Default> - consteval auto* GetLightCompareType() { - if constexpr (HasLightCompareType<T>) { - return static_cast<typename T::LightCompareType*>(nullptr); + consteval auto* GetRedBlackKeyType() { + if constexpr (HasRedBlackKeyType<T>) { + return static_cast<typename T::RedBlackKeyType*>(nullptr); } else { return static_cast<Default*>(nullptr); } @@ -252,16 +261,17 @@ namespace impl { } // namespace impl template <typename T, typename Default> -using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; +using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>; template <class T, class Traits, class Comparator> class IntrusiveRedBlackTree { + YUZU_NON_COPYABLE(IntrusiveRedBlackTree); public: using ImplType = impl::IntrusiveRedBlackTreeImpl; private: - ImplType impl{}; + ImplType m_impl; public: template <bool Const> @@ -277,9 +287,9 @@ public: using iterator = Iterator<false>; using const_iterator = Iterator<true>; - using light_value_type = LightCompareType<Comparator, value_type>; - using const_light_pointer = const light_value_type*; - using const_light_reference = const light_value_type&; + using key_type = RedBlackKeyType<Comparator, value_type>; + using const_key_pointer = const key_type*; + using const_key_reference = const key_type&; template <bool Const> class Iterator { @@ -298,183 +308,201 @@ public: IntrusiveRedBlackTree::reference>; private: - ImplIterator iterator; + ImplIterator m_impl; private: - explicit Iterator(ImplIterator it) : iterator(it) {} + constexpr explicit Iterator(ImplIterator it) : m_impl(it) {} - explicit Iterator(typename std::conditional<Const, ImplType::const_iterator, - ImplType::iterator>::type::pointer ptr) - : iterator(ptr) {} + constexpr explicit Iterator(typename ImplIterator::pointer p) : m_impl(p) {} - ImplIterator GetImplIterator() const { - return this->iterator; + constexpr ImplIterator GetImplIterator() const { + return m_impl; } public: - bool operator==(const Iterator& rhs) const { - return this->iterator == rhs.iterator; + constexpr bool operator==(const Iterator& rhs) const { + return m_impl == rhs.m_impl; } - bool operator!=(const Iterator& rhs) const { + constexpr bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } - pointer operator->() const { - return Traits::GetParent(std::addressof(*this->iterator)); + constexpr pointer operator->() const { + return Traits::GetParent(std::addressof(*m_impl)); } - reference operator*() const { - return *Traits::GetParent(std::addressof(*this->iterator)); + constexpr reference operator*() const { + return *Traits::GetParent(std::addressof(*m_impl)); } - Iterator& operator++() { - ++this->iterator; + constexpr Iterator& operator++() { + ++m_impl; return *this; } - Iterator& operator--() { - --this->iterator; + constexpr Iterator& operator--() { + --m_impl; return *this; } - Iterator operator++(int) { + constexpr Iterator operator++(int) { const Iterator it{*this}; - ++this->iterator; + ++m_impl; return it; } - Iterator operator--(int) { + constexpr Iterator operator--(int) { const Iterator it{*this}; - --this->iterator; + --m_impl; return it; } - operator Iterator<true>() const { - return Iterator<true>(this->iterator); + constexpr operator Iterator<true>() const { + return Iterator<true>(m_impl); } }; private: - static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, - const IntrusiveRedBlackTreeNode* rhs) { + static constexpr int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, + const IntrusiveRedBlackTreeNode* rhs) { return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); } - static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) { - return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs)); + static constexpr int CompareKeyImpl(const_key_reference key, + const IntrusiveRedBlackTreeNode* rhs) { + return Comparator::Compare(key, *Traits::GetParent(rhs)); } // Define accessors using RB_* functions. - IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { - return RB_INSERT(&impl.root, node, CompareImpl); + constexpr IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { + return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl); } - IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { - return RB_FIND(const_cast<ImplType::RootType*>(&impl.root), - const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); + constexpr IntrusiveRedBlackTreeNode* FindImpl(IntrusiveRedBlackTreeNode const* node) const { + return freebsd::RB_FIND(const_cast<ImplType::RootType&>(m_impl.m_root), + const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); } - IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { - return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root), - const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); + constexpr IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* node) const { + return freebsd::RB_NFIND(const_cast<ImplType::RootType&>(m_impl.m_root), + const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); } - IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { - return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), - static_cast<const void*>(lelm), LightCompareImpl); + constexpr IntrusiveRedBlackTreeNode* FindKeyImpl(const_key_reference key) const { + return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, + CompareKeyImpl); } - IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { - return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), - static_cast<const void*>(lelm), LightCompareImpl); + constexpr IntrusiveRedBlackTreeNode* NFindKeyImpl(const_key_reference key) const { + return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, + CompareKeyImpl); + } + + constexpr IntrusiveRedBlackTreeNode* FindExistingImpl( + IntrusiveRedBlackTreeNode const* node) const { + return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType&>(m_impl.m_root), + const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); + } + + constexpr IntrusiveRedBlackTreeNode* FindExistingKeyImpl(const_key_reference key) const { + return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, + CompareKeyImpl); } public: constexpr IntrusiveRedBlackTree() = default; // Iterator accessors. - iterator begin() { - return iterator(this->impl.begin()); + constexpr iterator begin() { + return iterator(m_impl.begin()); } - const_iterator begin() const { - return const_iterator(this->impl.begin()); + constexpr const_iterator begin() const { + return const_iterator(m_impl.begin()); } - iterator end() { - return iterator(this->impl.end()); + constexpr iterator end() { + return iterator(m_impl.end()); } - const_iterator end() const { - return const_iterator(this->impl.end()); + constexpr const_iterator end() const { + return const_iterator(m_impl.end()); } - const_iterator cbegin() const { + constexpr const_iterator cbegin() const { return this->begin(); } - const_iterator cend() const { + constexpr const_iterator cend() const { return this->end(); } - iterator iterator_to(reference ref) { - return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); + constexpr iterator iterator_to(reference ref) { + return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); } - const_iterator iterator_to(const_reference ref) const { - return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); + constexpr const_iterator iterator_to(const_reference ref) const { + return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); } // Content management. - bool empty() const { - return this->impl.empty(); + constexpr bool empty() const { + return m_impl.empty(); } - reference back() { - return *Traits::GetParent(std::addressof(this->impl.back())); + constexpr reference back() { + return *Traits::GetParent(std::addressof(m_impl.back())); } - const_reference back() const { - return *Traits::GetParent(std::addressof(this->impl.back())); + constexpr const_reference back() const { + return *Traits::GetParent(std::addressof(m_impl.back())); } - reference front() { - return *Traits::GetParent(std::addressof(this->impl.front())); + constexpr reference front() { + return *Traits::GetParent(std::addressof(m_impl.front())); } - const_reference front() const { - return *Traits::GetParent(std::addressof(this->impl.front())); + constexpr const_reference front() const { + return *Traits::GetParent(std::addressof(m_impl.front())); } - iterator erase(iterator it) { - return iterator(this->impl.erase(it.GetImplIterator())); + constexpr iterator erase(iterator it) { + return iterator(m_impl.erase(it.GetImplIterator())); } - iterator insert(reference ref) { + constexpr iterator insert(reference ref) { ImplType::pointer node = Traits::GetNode(std::addressof(ref)); this->InsertImpl(node); return iterator(node); } - iterator find(const_reference ref) const { + constexpr iterator find(const_reference ref) const { return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); } - iterator nfind(const_reference ref) const { + constexpr iterator nfind(const_reference ref) const { return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); } - iterator find_light(const_light_reference ref) const { - return iterator(this->FindLightImpl(std::addressof(ref))); + constexpr iterator find_key(const_key_reference ref) const { + return iterator(this->FindKeyImpl(ref)); + } + + constexpr iterator nfind_key(const_key_reference ref) const { + return iterator(this->NFindKeyImpl(ref)); + } + + constexpr iterator find_existing(const_reference ref) const { + return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref)))); } - iterator nfind_light(const_light_reference ref) const { - return iterator(this->NFindLightImpl(std::addressof(ref))); + constexpr iterator find_existing_key(const_key_reference ref) const { + return iterator(this->FindExistingKeyImpl(ref)); } }; -template <auto T, class Derived = impl::GetParentType<T>> +template <auto T, class Derived = Common::impl::GetParentType<T>> class IntrusiveRedBlackTreeMemberTraits; template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> @@ -498,19 +526,16 @@ private: return std::addressof(parent->*Member); } - static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { - return GetParentPointer<Member, Derived>(node); + static Derived* GetParent(IntrusiveRedBlackTreeNode* node) { + return Common::GetParentPointer<Member, Derived>(node); } - static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { - return GetParentPointer<Member, Derived>(node); + static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { + return Common::GetParentPointer<Member, Derived>(node); } - -private: - static constexpr TypedStorage<Derived> DerivedStorage = {}; }; -template <auto T, class Derived = impl::GetParentType<T>> +template <auto T, class Derived = Common::impl::GetParentType<T>> class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> @@ -521,11 +546,6 @@ public: IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; - static constexpr bool IsValid() { - TypedStorage<Derived> DerivedStorage = {}; - return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage); - } - private: template <class, class, class> friend class IntrusiveRedBlackTree; @@ -540,30 +560,36 @@ private: return std::addressof(parent->*Member); } - static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { - return GetParentPointer<Member, Derived>(node); + static Derived* GetParent(IntrusiveRedBlackTreeNode* node) { + return Common::GetParentPointer<Member, Derived>(node); } - static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { - return GetParentPointer<Member, Derived>(node); + static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { + return Common::GetParentPointer<Member, Derived>(node); } }; template <class Derived> -class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { +class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { public: + using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode; + constexpr Derived* GetPrev() { - return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); + return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>( + impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); } constexpr const Derived* GetPrev() const { - return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); + return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>( + impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); } constexpr Derived* GetNext() { - return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); + return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>( + impl::IntrusiveRedBlackTreeImpl::GetNext(this))); } constexpr const Derived* GetNext() const { - return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); + return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>( + impl::IntrusiveRedBlackTreeImpl::GetNext(this))); } }; @@ -581,19 +607,22 @@ private: friend class impl::IntrusiveRedBlackTreeImpl; static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { - return static_cast<IntrusiveRedBlackTreeNode*>(parent); + return static_cast<IntrusiveRedBlackTreeNode*>( + static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(parent)); } static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { - return static_cast<const IntrusiveRedBlackTreeNode*>(parent); + return static_cast<const IntrusiveRedBlackTreeNode*>( + static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(parent)); } static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { - return static_cast<Derived*>(node); + return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(node)); } - static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { - return static_cast<const Derived*>(node); + static constexpr Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { + return static_cast<const Derived*>( + static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(node)); } }; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index c51c05b28..4a2462ec4 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -218,19 +218,17 @@ private: Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) : filter{filter_}, file_backend{file_backend_filename} {} - ~Impl() { - StopBackendThread(); - } + ~Impl() = default; void StartBackendThread() { - backend_thread = std::thread([this] { + backend_thread = std::jthread([this](std::stop_token stop_token) { Common::SetCurrentThreadName("yuzu:Log"); Entry entry; const auto write_logs = [this, &entry]() { ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); }; - while (!stop.stop_requested()) { - entry = message_queue.PopWait(stop.get_token()); + while (!stop_token.stop_requested()) { + entry = message_queue.PopWait(stop_token); if (entry.filename != nullptr) { write_logs(); } @@ -244,11 +242,6 @@ private: }); } - void StopBackendThread() { - stop.request_stop(); - backend_thread.join(); - } - Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, std::string&& message) const { using std::chrono::duration_cast; @@ -283,10 +276,9 @@ private: ColorConsoleBackend color_console_backend{}; FileBackend file_backend; - std::stop_source stop; - std::thread backend_thread; MPSCQueue<Entry, true> message_queue{}; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; + std::jthread backend_thread; }; } // namespace diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index b898a652c..4afc1369a 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Service, Migration) \ SUB(Service, Mii) \ SUB(Service, MM) \ + SUB(Service, MNPP) \ SUB(Service, NCM) \ SUB(Service, NFC) \ SUB(Service, NFP) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 9ed0c7ad6..2b6e4daa7 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -76,6 +76,7 @@ enum class Class : u8 { Service_Migration, ///< The migration service Service_Mii, ///< The Mii service Service_MM, ///< The MM (Multimedia) service + Service_MNPP, ///< The MNPP service Service_NCM, ///< The NCM service Service_NFC, ///< The NFC (Near-field communication) service Service_NFP, ///< The NFP service diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 9fffd816f..4817b09f9 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp @@ -10,11 +10,65 @@ PageTable::PageTable() = default; PageTable::~PageTable() noexcept = default; -void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { - const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; +bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, + u64 address) const { + // Setup invalid defaults. + out_entry.phys_addr = 0; + out_entry.block_size = page_size; + out_context.next_page = 0; + + // Validate that we can read the actual entry. + const auto page = address / page_size; + if (page >= backing_addr.size()) { + return false; + } + + // Validate that the entry is mapped. + const auto phys_addr = backing_addr[page]; + if (phys_addr == 0) { + return false; + } + + // Populate the results. + out_entry.phys_addr = phys_addr + address; + out_context.next_page = page + 1; + out_context.next_offset = address + page_size; + + return true; +} + +bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const { + // Setup invalid defaults. + out_entry.phys_addr = 0; + out_entry.block_size = page_size; + + // Validate that we can read the actual entry. + const auto page = context.next_page; + if (page >= backing_addr.size()) { + return false; + } + + // Validate that the entry is mapped. + const auto phys_addr = backing_addr[page]; + if (phys_addr == 0) { + return false; + } + + // Populate the results. + out_entry.phys_addr = phys_addr + context.next_offset; + context.next_page = page + 1; + context.next_offset += page_size; + + return true; +} + +void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) { + const std::size_t num_page_table_entries{1ULL + << (address_space_width_in_bits - page_size_in_bits)}; pointers.resize(num_page_table_entries); backing_addr.resize(num_page_table_entries); current_address_space_width_in_bits = address_space_width_in_bits; + page_size = 1ULL << page_size_in_bits; } } // namespace Common diff --git a/src/common/page_table.h b/src/common/page_table.h index 8267e8b4d..82d91e9f3 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h @@ -27,6 +27,16 @@ enum class PageType : u8 { * mimics the way a real CPU page table works. */ struct PageTable { + struct TraversalEntry { + u64 phys_addr{}; + std::size_t block_size{}; + }; + + struct TraversalContext { + u64 next_page{}; + u64 next_offset{}; + }; + /// Number of bits reserved for attribute tagging. /// This can be at most the guaranteed alignment of the pointers in the page table. static constexpr int ATTRIBUTE_BITS = 2; @@ -89,6 +99,10 @@ struct PageTable { PageTable(PageTable&&) noexcept = default; PageTable& operator=(PageTable&&) noexcept = default; + bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, + u64 address) const; + bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const; + /** * Resizes the page table to be able to accommodate enough pages within * a given address space. @@ -96,9 +110,9 @@ struct PageTable { * @param address_space_width_in_bits The address size width in bits. * @param page_size_in_bits The page size in bits. */ - void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); + void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits); - size_t GetAddressSpaceBits() const { + std::size_t GetAddressSpaceBits() const { return current_address_space_width_in_bits; } @@ -110,9 +124,11 @@ struct PageTable { VirtualBuffer<u64> backing_addr; - size_t current_address_space_width_in_bits; + std::size_t current_address_space_width_in_bits{}; + + u8* fastmem_arena{}; - u8* fastmem_arena; + std::size_t page_size{}; }; } // namespace Common diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 6964a8273..877e0faa4 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -167,6 +167,7 @@ void RestoreGlobalState(bool is_powered_on) { // Core values.use_multi_core.SetGlobal(true); + values.use_extended_memory_layout.SetGlobal(true); // CPU values.cpu_accuracy.SetGlobal(true); @@ -175,6 +176,7 @@ void RestoreGlobalState(bool is_powered_on) { values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true); values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); values.cpuopt_unsafe_fastmem_check.SetGlobal(true); + values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true); // Renderer values.renderer_backend.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 9bee6e10f..a37d83fb3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -466,6 +466,7 @@ struct Values { // Core Setting<bool> use_multi_core{true, "use_multi_core"}; + Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; // Cpu RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, @@ -483,12 +484,15 @@ struct Values { BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; + BasicSetting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; + BasicSetting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; + Setting<bool> cpuopt_unsafe_ignore_global_monitor{true, "cpuopt_unsafe_ignore_global_monitor"}; // Renderer RangedSetting<RendererBackend> renderer_backend{ diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index 6241d08b3..98c82cd17 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp @@ -55,22 +55,50 @@ void AppendBuildInfo(FieldCollection& fc) { void AppendCPUInfo(FieldCollection& fc) { #ifdef ARCHITECTURE_x86_64 - fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); - fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1); - fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2); + + const auto& caps = Common::GetCPUCaps(); + const auto add_field = [&fc](std::string_view field_name, const auto& field_value) { + fc.AddField(FieldType::UserSystem, field_name, field_value); + }; + add_field("CPU_Model", caps.cpu_string); + add_field("CPU_BrandString", caps.brand_string); + + add_field("CPU_Extension_x64_SSE", caps.sse); + add_field("CPU_Extension_x64_SSE2", caps.sse2); + add_field("CPU_Extension_x64_SSE3", caps.sse3); + add_field("CPU_Extension_x64_SSSE3", caps.ssse3); + add_field("CPU_Extension_x64_SSE41", caps.sse4_1); + add_field("CPU_Extension_x64_SSE42", caps.sse4_2); + + add_field("CPU_Extension_x64_AVX", caps.avx); + add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni); + add_field("CPU_Extension_x64_AVX2", caps.avx2); + + // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field + add_field("CPU_Extension_x64_AVX512", + caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw); + + add_field("CPU_Extension_x64_AVX512F", caps.avx512f); + add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd); + add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl); + add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq); + add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw); + add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg); + add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi); + + add_field("CPU_Extension_x64_AES", caps.aes); + add_field("CPU_Extension_x64_BMI1", caps.bmi1); + add_field("CPU_Extension_x64_BMI2", caps.bmi2); + add_field("CPU_Extension_x64_F16C", caps.f16c); + add_field("CPU_Extension_x64_FMA", caps.fma); + add_field("CPU_Extension_x64_FMA4", caps.fma4); + add_field("CPU_Extension_x64_GFNI", caps.gfni); + add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc); + add_field("CPU_Extension_x64_LZCNT", caps.lzcnt); + add_field("CPU_Extension_x64_MOVBE", caps.movbe); + add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); + add_field("CPU_Extension_x64_POPCNT", caps.popcnt); + add_field("CPU_Extension_x64_SHA", caps.sha); #else fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); #endif diff --git a/src/common/telemetry.h b/src/common/telemetry.h index 49186e848..3524c857e 100644 --- a/src/common/telemetry.h +++ b/src/common/telemetry.h @@ -8,6 +8,8 @@ #include <map> #include <memory> #include <string> +#include <string_view> +#include "common/common_funcs.h" #include "common/common_types.h" namespace Common::Telemetry { @@ -28,7 +30,7 @@ struct VisitorInterface; /** * Interface class for telemetry data fields. */ -class FieldInterface : NonCopyable { +class FieldInterface { public: virtual ~FieldInterface() = default; @@ -52,14 +54,15 @@ public: template <typename T> class Field : public FieldInterface { public: - Field(FieldType type_, std::string name_, T value_) - : name(std::move(name_)), type(type_), value(std::move(value_)) {} + YUZU_NON_COPYABLE(Field); - Field(const Field&) = default; - Field& operator=(const Field&) = default; + Field(FieldType type_, std::string_view name_, T value_) + : name(name_), type(type_), value(std::move(value_)) {} - Field(Field&&) = default; - Field& operator=(Field&& other) = default; + ~Field() override = default; + + Field(Field&&) noexcept = default; + Field& operator=(Field&& other) noexcept = default; void Accept(VisitorInterface& visitor) const override; @@ -98,9 +101,15 @@ private: /** * Collection of data fields that have been logged. */ -class FieldCollection final : NonCopyable { +class FieldCollection final { public: + YUZU_NON_COPYABLE(FieldCollection); + FieldCollection() = default; + ~FieldCollection() = default; + + FieldCollection(FieldCollection&&) noexcept = default; + FieldCollection& operator=(FieldCollection&&) noexcept = default; /** * Accept method for the visitor pattern, visits each field in the collection. @@ -115,7 +124,7 @@ public: * @param value Value for the field to add. */ template <typename T> - void AddField(FieldType type, const char* name, T value) { + void AddField(FieldType type, std::string_view name, T value) { return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); } @@ -133,7 +142,7 @@ private: * Telemetry fields visitor interface class. A backend to log to a web service should implement * this interface. */ -struct VisitorInterface : NonCopyable { +struct VisitorInterface { virtual ~VisitorInterface() = default; virtual void Visit(const Field<bool>& field) = 0; @@ -160,8 +169,11 @@ struct VisitorInterface : NonCopyable { * Empty implementation of VisitorInterface that drops all fields. Used when a functional * backend implementation is not available. */ -struct NullVisitor : public VisitorInterface { - ~NullVisitor() = default; +struct NullVisitor final : public VisitorInterface { + YUZU_NON_COPYABLE(NullVisitor); + + NullVisitor() = default; + ~NullVisitor() override = default; void Visit(const Field<bool>& /*field*/) override {} void Visit(const Field<double>& /*field*/) override {} diff --git a/src/common/tree.h b/src/common/tree.h index 18faa4a48..28370e343 100644 --- a/src/common/tree.h +++ b/src/common/tree.h @@ -43,294 +43,265 @@ * The maximum height of a red-black tree is 2lg (n+1). */ -#include "common/assert.h" +namespace Common::freebsd { -namespace Common { +enum class RBColor { + RB_BLACK = 0, + RB_RED = 1, +}; + +#pragma pack(push, 4) template <typename T> -class RBHead { +class RBEntry { public: - [[nodiscard]] T* Root() { - return rbh_root; - } + constexpr RBEntry() = default; - [[nodiscard]] const T* Root() const { - return rbh_root; + [[nodiscard]] constexpr T* Left() { + return m_rbe_left; } - - void SetRoot(T* root) { - rbh_root = root; + [[nodiscard]] constexpr const T* Left() const { + return m_rbe_left; } - [[nodiscard]] bool IsEmpty() const { - return Root() == nullptr; + constexpr void SetLeft(T* e) { + m_rbe_left = e; } -private: - T* rbh_root = nullptr; -}; - -enum class EntryColor { - Black, - Red, -}; - -template <typename T> -class RBEntry { -public: - [[nodiscard]] T* Left() { - return rbe_left; + [[nodiscard]] constexpr T* Right() { + return m_rbe_right; } - - [[nodiscard]] const T* Left() const { - return rbe_left; + [[nodiscard]] constexpr const T* Right() const { + return m_rbe_right; } - void SetLeft(T* left) { - rbe_left = left; + constexpr void SetRight(T* e) { + m_rbe_right = e; } - [[nodiscard]] T* Right() { - return rbe_right; + [[nodiscard]] constexpr T* Parent() { + return m_rbe_parent; } - - [[nodiscard]] const T* Right() const { - return rbe_right; + [[nodiscard]] constexpr const T* Parent() const { + return m_rbe_parent; } - void SetRight(T* right) { - rbe_right = right; + constexpr void SetParent(T* e) { + m_rbe_parent = e; } - [[nodiscard]] T* Parent() { - return rbe_parent; + [[nodiscard]] constexpr bool IsBlack() const { + return m_rbe_color == RBColor::RB_BLACK; } - - [[nodiscard]] const T* Parent() const { - return rbe_parent; + [[nodiscard]] constexpr bool IsRed() const { + return m_rbe_color == RBColor::RB_RED; } - - void SetParent(T* parent) { - rbe_parent = parent; + [[nodiscard]] constexpr RBColor Color() const { + return m_rbe_color; } - [[nodiscard]] bool IsBlack() const { - return rbe_color == EntryColor::Black; + constexpr void SetColor(RBColor c) { + m_rbe_color = c; } - [[nodiscard]] bool IsRed() const { - return rbe_color == EntryColor::Red; - } +private: + T* m_rbe_left{}; + T* m_rbe_right{}; + T* m_rbe_parent{}; + RBColor m_rbe_color{RBColor::RB_BLACK}; +}; +#pragma pack(pop) - [[nodiscard]] EntryColor Color() const { - return rbe_color; - } +template <typename T> +struct CheckRBEntry { + static constexpr bool value = false; +}; +template <typename T> +struct CheckRBEntry<RBEntry<T>> { + static constexpr bool value = true; +}; - void SetColor(EntryColor color) { - rbe_color = color; - } +template <typename T> +concept IsRBEntry = CheckRBEntry<T>::value; +template <typename T> +concept HasRBEntry = requires(T& t, const T& ct) { + { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; + { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; +}; + +template <typename T> +requires HasRBEntry<T> +class RBHead { private: - T* rbe_left = nullptr; - T* rbe_right = nullptr; - T* rbe_parent = nullptr; - EntryColor rbe_color{}; + T* m_rbh_root = nullptr; + +public: + [[nodiscard]] constexpr T* Root() { + return m_rbh_root; + } + [[nodiscard]] constexpr const T* Root() const { + return m_rbh_root; + } + constexpr void SetRoot(T* root) { + m_rbh_root = root; + } + + [[nodiscard]] constexpr bool IsEmpty() const { + return this->Root() == nullptr; + } }; -template <typename Node> -[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { - return node->GetEntry(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { + return t->GetRBEntry(); } - -template <typename Node> -[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) { - return node->GetEntry(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { + return t->GetRBEntry(); } -template <typename Node> -[[nodiscard]] Node* RB_PARENT(Node* node) { - return RB_ENTRY(node).Parent(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr T* RB_LEFT(T* t) { + return RB_ENTRY(t).Left(); } - -template <typename Node> -[[nodiscard]] const Node* RB_PARENT(const Node* node) { - return RB_ENTRY(node).Parent(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr const T* RB_LEFT(const T* t) { + return RB_ENTRY(t).Left(); } -template <typename Node> -void RB_SET_PARENT(Node* node, Node* parent) { - return RB_ENTRY(node).SetParent(parent); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr T* RB_RIGHT(T* t) { + return RB_ENTRY(t).Right(); } - -template <typename Node> -[[nodiscard]] Node* RB_LEFT(Node* node) { - return RB_ENTRY(node).Left(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { + return RB_ENTRY(t).Right(); } -template <typename Node> -[[nodiscard]] const Node* RB_LEFT(const Node* node) { - return RB_ENTRY(node).Left(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr T* RB_PARENT(T* t) { + return RB_ENTRY(t).Parent(); } - -template <typename Node> -void RB_SET_LEFT(Node* node, Node* left) { - return RB_ENTRY(node).SetLeft(left); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr const T* RB_PARENT(const T* t) { + return RB_ENTRY(t).Parent(); } -template <typename Node> -[[nodiscard]] Node* RB_RIGHT(Node* node) { - return RB_ENTRY(node).Right(); +template <typename T> +requires HasRBEntry<T> +constexpr void RB_SET_LEFT(T* t, T* e) { + RB_ENTRY(t).SetLeft(e); } - -template <typename Node> -[[nodiscard]] const Node* RB_RIGHT(const Node* node) { - return RB_ENTRY(node).Right(); +template <typename T> +requires HasRBEntry<T> +constexpr void RB_SET_RIGHT(T* t, T* e) { + RB_ENTRY(t).SetRight(e); } - -template <typename Node> -void RB_SET_RIGHT(Node* node, Node* right) { - return RB_ENTRY(node).SetRight(right); +template <typename T> +requires HasRBEntry<T> +constexpr void RB_SET_PARENT(T* t, T* e) { + RB_ENTRY(t).SetParent(e); } -template <typename Node> -[[nodiscard]] bool RB_IS_BLACK(const Node* node) { - return RB_ENTRY(node).IsBlack(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { + return RB_ENTRY(t).IsBlack(); } - -template <typename Node> -[[nodiscard]] bool RB_IS_RED(const Node* node) { - return RB_ENTRY(node).IsRed(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr bool RB_IS_RED(const T* t) { + return RB_ENTRY(t).IsRed(); } -template <typename Node> -[[nodiscard]] EntryColor RB_COLOR(const Node* node) { - return RB_ENTRY(node).Color(); +template <typename T> +requires HasRBEntry<T> +[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { + return RB_ENTRY(t).Color(); } -template <typename Node> -void RB_SET_COLOR(Node* node, EntryColor color) { - return RB_ENTRY(node).SetColor(color); +template <typename T> +requires HasRBEntry<T> +constexpr void RB_SET_COLOR(T* t, RBColor c) { + RB_ENTRY(t).SetColor(c); } -template <typename Node> -void RB_SET(Node* node, Node* parent) { - auto& entry = RB_ENTRY(node); - entry.SetParent(parent); - entry.SetLeft(nullptr); - entry.SetRight(nullptr); - entry.SetColor(EntryColor::Red); +template <typename T> +requires HasRBEntry<T> +constexpr void RB_SET(T* elm, T* parent) { + auto& rb_entry = RB_ENTRY(elm); + rb_entry.SetParent(parent); + rb_entry.SetLeft(nullptr); + rb_entry.SetRight(nullptr); + rb_entry.SetColor(RBColor::RB_RED); } -template <typename Node> -void RB_SET_BLACKRED(Node* black, Node* red) { - RB_SET_COLOR(black, EntryColor::Black); - RB_SET_COLOR(red, EntryColor::Red); +template <typename T> +requires HasRBEntry<T> +constexpr void RB_SET_BLACKRED(T* black, T* red) { + RB_SET_COLOR(black, RBColor::RB_BLACK); + RB_SET_COLOR(red, RBColor::RB_RED); } -template <typename Node> -void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { +template <typename T> +requires HasRBEntry<T> +constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { tmp = RB_RIGHT(elm); - RB_SET_RIGHT(elm, RB_LEFT(tmp)); - if (RB_RIGHT(elm) != nullptr) { + if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) { RB_SET_PARENT(RB_LEFT(tmp), elm); } - RB_SET_PARENT(tmp, RB_PARENT(elm)); - if (RB_PARENT(tmp) != nullptr) { + if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) { if (elm == RB_LEFT(RB_PARENT(elm))) { RB_SET_LEFT(RB_PARENT(elm), tmp); } else { RB_SET_RIGHT(RB_PARENT(elm), tmp); } } else { - head->SetRoot(tmp); + head.SetRoot(tmp); } RB_SET_LEFT(tmp, elm); RB_SET_PARENT(elm, tmp); } -template <typename Node> -void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { +template <typename T> +requires HasRBEntry<T> +constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { tmp = RB_LEFT(elm); - RB_SET_LEFT(elm, RB_RIGHT(tmp)); - if (RB_LEFT(elm) != nullptr) { + if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) { RB_SET_PARENT(RB_RIGHT(tmp), elm); } - RB_SET_PARENT(tmp, RB_PARENT(elm)); - if (RB_PARENT(tmp) != nullptr) { + if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) { if (elm == RB_LEFT(RB_PARENT(elm))) { RB_SET_LEFT(RB_PARENT(elm), tmp); } else { RB_SET_RIGHT(RB_PARENT(elm), tmp); } } else { - head->SetRoot(tmp); + head.SetRoot(tmp); } RB_SET_RIGHT(tmp, elm); RB_SET_PARENT(elm, tmp); } -template <typename Node> -void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { - Node* parent = nullptr; - Node* tmp = nullptr; - - while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { - Node* gparent = RB_PARENT(parent); - if (parent == RB_LEFT(gparent)) { - tmp = RB_RIGHT(gparent); - if (tmp && RB_IS_RED(tmp)) { - RB_SET_COLOR(tmp, EntryColor::Black); - RB_SET_BLACKRED(parent, gparent); - elm = gparent; - continue; - } - - if (RB_RIGHT(parent) == elm) { - RB_ROTATE_LEFT(head, parent, tmp); - tmp = parent; - parent = elm; - elm = tmp; - } - - RB_SET_BLACKRED(parent, gparent); - RB_ROTATE_RIGHT(head, gparent, tmp); - } else { - tmp = RB_LEFT(gparent); - if (tmp && RB_IS_RED(tmp)) { - RB_SET_COLOR(tmp, EntryColor::Black); - RB_SET_BLACKRED(parent, gparent); - elm = gparent; - continue; - } - - if (RB_LEFT(parent) == elm) { - RB_ROTATE_RIGHT(head, parent, tmp); - tmp = parent; - parent = elm; - elm = tmp; - } - - RB_SET_BLACKRED(parent, gparent); - RB_ROTATE_LEFT(head, gparent, tmp); - } - } - - RB_SET_COLOR(head->Root(), EntryColor::Black); -} - -template <typename Node> -void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { - Node* tmp; - while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root() && parent != nullptr) { +template <typename T> +requires HasRBEntry<T> +constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { + T* tmp; + while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) { if (RB_LEFT(parent) == elm) { tmp = RB_RIGHT(parent); - if (!tmp) { - ASSERT_MSG(false, "tmp is invalid!"); - break; - } if (RB_IS_RED(tmp)) { RB_SET_BLACKRED(tmp, parent); RB_ROTATE_LEFT(head, parent, tmp); @@ -339,29 +310,29 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { - RB_SET_COLOR(tmp, EntryColor::Red); + RB_SET_COLOR(tmp, RBColor::RB_RED); elm = parent; parent = RB_PARENT(elm); } else { if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { - Node* oleft; + T* oleft; if ((oleft = RB_LEFT(tmp)) != nullptr) { - RB_SET_COLOR(oleft, EntryColor::Black); + RB_SET_COLOR(oleft, RBColor::RB_BLACK); } - RB_SET_COLOR(tmp, EntryColor::Red); + RB_SET_COLOR(tmp, RBColor::RB_RED); RB_ROTATE_RIGHT(head, tmp, oleft); tmp = RB_RIGHT(parent); } RB_SET_COLOR(tmp, RB_COLOR(parent)); - RB_SET_COLOR(parent, EntryColor::Black); + RB_SET_COLOR(parent, RBColor::RB_BLACK); if (RB_RIGHT(tmp)) { - RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black); + RB_SET_COLOR(RB_RIGHT(tmp), RBColor::RB_BLACK); } RB_ROTATE_LEFT(head, parent, tmp); - elm = head->Root(); + elm = head.Root(); break; } } else { @@ -372,68 +343,56 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { tmp = RB_LEFT(parent); } - if (!tmp) { - ASSERT_MSG(false, "tmp is invalid!"); - break; - } - if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { - RB_SET_COLOR(tmp, EntryColor::Red); + RB_SET_COLOR(tmp, RBColor::RB_RED); elm = parent; parent = RB_PARENT(elm); } else { if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { - Node* oright; + T* oright; if ((oright = RB_RIGHT(tmp)) != nullptr) { - RB_SET_COLOR(oright, EntryColor::Black); + RB_SET_COLOR(oright, RBColor::RB_BLACK); } - RB_SET_COLOR(tmp, EntryColor::Red); + RB_SET_COLOR(tmp, RBColor::RB_RED); RB_ROTATE_LEFT(head, tmp, oright); tmp = RB_LEFT(parent); } RB_SET_COLOR(tmp, RB_COLOR(parent)); - RB_SET_COLOR(parent, EntryColor::Black); + RB_SET_COLOR(parent, RBColor::RB_BLACK); if (RB_LEFT(tmp)) { - RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black); + RB_SET_COLOR(RB_LEFT(tmp), RBColor::RB_BLACK); } RB_ROTATE_RIGHT(head, parent, tmp); - elm = head->Root(); + elm = head.Root(); break; } } } if (elm) { - RB_SET_COLOR(elm, EntryColor::Black); + RB_SET_COLOR(elm, RBColor::RB_BLACK); } } -template <typename Node> -Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { - Node* child = nullptr; - Node* parent = nullptr; - Node* old = elm; - EntryColor color{}; - - const auto finalize = [&] { - if (color == EntryColor::Black) { - RB_REMOVE_COLOR(head, parent, child); - } - - return old; - }; +template <typename T> +requires HasRBEntry<T> +constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { + T* child = nullptr; + T* parent = nullptr; + T* old = elm; + RBColor color = RBColor::RB_BLACK; if (RB_LEFT(elm) == nullptr) { child = RB_RIGHT(elm); } else if (RB_RIGHT(elm) == nullptr) { child = RB_LEFT(elm); } else { - Node* left; + T* left; elm = RB_RIGHT(elm); while ((left = RB_LEFT(elm)) != nullptr) { elm = left; @@ -446,6 +405,7 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { if (child) { RB_SET_PARENT(child, parent); } + if (parent) { if (RB_LEFT(parent) == elm) { RB_SET_LEFT(parent, child); @@ -453,14 +413,14 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { RB_SET_RIGHT(parent, child); } } else { - head->SetRoot(child); + head.SetRoot(child); } if (RB_PARENT(elm) == old) { parent = elm; } - elm->SetEntry(old->GetEntry()); + elm->SetRBEntry(old->GetRBEntry()); if (RB_PARENT(old)) { if (RB_LEFT(RB_PARENT(old)) == old) { @@ -469,17 +429,24 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { RB_SET_RIGHT(RB_PARENT(old), elm); } } else { - head->SetRoot(elm); + head.SetRoot(elm); } + RB_SET_PARENT(RB_LEFT(old), elm); + if (RB_RIGHT(old)) { RB_SET_PARENT(RB_RIGHT(old), elm); } + if (parent) { left = parent; } - return finalize(); + if (color == RBColor::RB_BLACK) { + RB_REMOVE_COLOR(head, parent, child); + } + + return old; } parent = RB_PARENT(elm); @@ -495,17 +462,69 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { RB_SET_RIGHT(parent, child); } } else { - head->SetRoot(child); + head.SetRoot(child); + } + + if (color == RBColor::RB_BLACK) { + RB_REMOVE_COLOR(head, parent, child); + } + + return old; +} + +template <typename T> +requires HasRBEntry<T> +constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { + T *parent = nullptr, *tmp = nullptr; + while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { + T* gparent = RB_PARENT(parent); + if (parent == RB_LEFT(gparent)) { + tmp = RB_RIGHT(gparent); + if (tmp && RB_IS_RED(tmp)) { + RB_SET_COLOR(tmp, RBColor::RB_BLACK); + RB_SET_BLACKRED(parent, gparent); + elm = gparent; + continue; + } + + if (RB_RIGHT(parent) == elm) { + RB_ROTATE_LEFT(head, parent, tmp); + tmp = parent; + parent = elm; + elm = tmp; + } + + RB_SET_BLACKRED(parent, gparent); + RB_ROTATE_RIGHT(head, gparent, tmp); + } else { + tmp = RB_LEFT(gparent); + if (tmp && RB_IS_RED(tmp)) { + RB_SET_COLOR(tmp, RBColor::RB_BLACK); + RB_SET_BLACKRED(parent, gparent); + elm = gparent; + continue; + } + + if (RB_LEFT(parent) == elm) { + RB_ROTATE_RIGHT(head, parent, tmp); + tmp = parent; + parent = elm; + elm = tmp; + } + + RB_SET_BLACKRED(parent, gparent); + RB_ROTATE_LEFT(head, gparent, tmp); + } } - return finalize(); + RB_SET_COLOR(head.Root(), RBColor::RB_BLACK); } -// Inserts a node into the RB tree -template <typename Node, typename CompareFunction> -Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { - Node* parent = nullptr; - Node* tmp = head->Root(); +template <typename T, typename Compare> +requires HasRBEntry<T> +constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { + T* parent = nullptr; + T* tmp = head.Root(); int comp = 0; while (tmp) { @@ -529,17 +548,17 @@ Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { RB_SET_RIGHT(parent, elm); } } else { - head->SetRoot(elm); + head.SetRoot(elm); } RB_INSERT_COLOR(head, elm); return nullptr; } -// Finds the node with the same key as elm -template <typename Node, typename CompareFunction> -Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { - Node* tmp = head->Root(); +template <typename T, typename Compare> +requires HasRBEntry<T> +constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { + T* tmp = head.Root(); while (tmp) { const int comp = cmp(elm, tmp); @@ -555,11 +574,11 @@ Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { return nullptr; } -// Finds the first node greater than or equal to the search key -template <typename Node, typename CompareFunction> -Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { - Node* tmp = head->Root(); - Node* res = nullptr; +template <typename T, typename Compare> +requires HasRBEntry<T> +constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { + T* tmp = head.Root(); + T* res = nullptr; while (tmp) { const int comp = cmp(elm, tmp); @@ -576,13 +595,13 @@ Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { return res; } -// Finds the node with the same key as lelm -template <typename Node, typename CompareFunction> -Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { - Node* tmp = head->Root(); +template <typename T, typename U, typename Compare> +requires HasRBEntry<T> +constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { + T* tmp = head.Root(); while (tmp) { - const int comp = lcmp(lelm, tmp); + const int comp = cmp(key, tmp); if (comp < 0) { tmp = RB_LEFT(tmp); } else if (comp > 0) { @@ -595,14 +614,14 @@ Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) return nullptr; } -// Finds the first node greater than or equal to the search key -template <typename Node, typename CompareFunction> -Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { - Node* tmp = head->Root(); - Node* res = nullptr; +template <typename T, typename U, typename Compare> +requires HasRBEntry<T> +constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { + T* tmp = head.Root(); + T* res = nullptr; while (tmp) { - const int comp = lcmp(lelm, tmp); + const int comp = cmp(key, tmp); if (comp < 0) { res = tmp; tmp = RB_LEFT(tmp); @@ -616,8 +635,43 @@ Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) return res; } -template <typename Node> -Node* RB_NEXT(Node* elm) { +template <typename T, typename Compare> +requires HasRBEntry<T> +constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { + T* tmp = head.Root(); + + while (true) { + const int comp = cmp(elm, tmp); + if (comp < 0) { + tmp = RB_LEFT(tmp); + } else if (comp > 0) { + tmp = RB_RIGHT(tmp); + } else { + return tmp; + } + } +} + +template <typename T, typename U, typename Compare> +requires HasRBEntry<T> +constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { + T* tmp = head.Root(); + + while (true) { + const int comp = cmp(key, tmp); + if (comp < 0) { + tmp = RB_LEFT(tmp); + } else if (comp > 0) { + tmp = RB_RIGHT(tmp); + } else { + return tmp; + } + } +} + +template <typename T> +requires HasRBEntry<T> +constexpr T* RB_NEXT(T* elm) { if (RB_RIGHT(elm)) { elm = RB_RIGHT(elm); while (RB_LEFT(elm)) { @@ -636,8 +690,9 @@ Node* RB_NEXT(Node* elm) { return elm; } -template <typename Node> -Node* RB_PREV(Node* elm) { +template <typename T> +requires HasRBEntry<T> +constexpr T* RB_PREV(T* elm) { if (RB_LEFT(elm)) { elm = RB_LEFT(elm); while (RB_RIGHT(elm)) { @@ -656,30 +711,32 @@ Node* RB_PREV(Node* elm) { return elm; } -template <typename Node> -Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { - Node* tmp = head->Root(); - Node* parent = nullptr; +template <typename T> +requires HasRBEntry<T> +constexpr T* RB_MIN(RBHead<T>& head) { + T* tmp = head.Root(); + T* parent = nullptr; while (tmp) { parent = tmp; - if (is_min) { - tmp = RB_LEFT(tmp); - } else { - tmp = RB_RIGHT(tmp); - } + tmp = RB_LEFT(tmp); } return parent; } -template <typename Node> -Node* RB_MIN(RBHead<Node>* head) { - return RB_MINMAX(head, true); -} +template <typename T> +requires HasRBEntry<T> +constexpr T* RB_MAX(RBHead<T>& head) { + T* tmp = head.Root(); + T* parent = nullptr; -template <typename Node> -Node* RB_MAX(RBHead<Node>* head) { - return RB_MINMAX(head, false); + while (tmp) { + parent = tmp; + tmp = RB_RIGHT(tmp); + } + + return parent; } -} // namespace Common + +} // namespace Common::freebsd diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index d7435a6e9..2b6a530e3 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp @@ -1,23 +1,25 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2022 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <bit> +#include <optional> #include <random> #include <fmt/format.h> #include "common/assert.h" +#include "common/tiny_mt.h" #include "common/uuid.h" namespace Common { namespace { -bool IsHexDigit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} +constexpr size_t RawStringSize = sizeof(UUID) * 2; +constexpr size_t FormattedStringSize = RawStringSize + 4; -u8 HexCharToByte(char c) { +std::optional<u8> HexCharToByte(char c) { if (c >= '0' && c <= '9') { return static_cast<u8>(c - '0'); } @@ -28,60 +30,184 @@ u8 HexCharToByte(char c) { return static_cast<u8>(c - 'A' + 10); } ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); - return u8{0}; + return std::nullopt; } -} // Anonymous namespace +std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) { + std::array<u8, 0x10> uuid; + + for (size_t i = 0; i < RawStringSize; i += 2) { + const auto upper = HexCharToByte(raw_string[i]); + const auto lower = HexCharToByte(raw_string[i + 1]); + if (!upper || !lower) { + return {}; + } + uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower); + } + + return uuid; +} -u128 HexStringToU128(std::string_view hex_string) { - const size_t length = hex_string.length(); +std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { + std::array<u8, 0x10> uuid; - // Detect "0x" prefix. - const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x'; - const size_t offset = has_0x_prefix ? 2 : 0; + size_t i = 0; - // Check length. - if (length > 32 + offset) { - ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!"); - return INVALID_UUID; + // Process the first 8 characters. + const auto* str = formatted_string.data(); + + for (; i < 4; ++i) { + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; + } + uuid[i] = static_cast<u8>((*upper << 4) | *lower); + } + + // Process the next 4 characters. + ++str; + + for (; i < 6; ++i) { + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; + } + uuid[i] = static_cast<u8>((*upper << 4) | *lower); } - u64 lo = 0; - u64 hi = 0; - for (size_t i = 0; i < length - offset; ++i) { - const char c = hex_string[length - 1 - i]; - if (!IsHexDigit(c)) { - ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); - return INVALID_UUID; + // Process the next 4 characters. + ++str; + + for (; i < 8; ++i) { + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; } - if (i < 16) { - lo |= u64{HexCharToByte(c)} << (i * 4); + uuid[i] = static_cast<u8>((*upper << 4) | *lower); + } + + // Process the next 4 characters. + ++str; + + for (; i < 10; ++i) { + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; } - if (i >= 16) { - hi |= u64{HexCharToByte(c)} << ((i - 16) * 4); + uuid[i] = static_cast<u8>((*upper << 4) | *lower); + } + + // Process the last 12 characters. + ++str; + + for (; i < 16; ++i) { + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; } + uuid[i] = static_cast<u8>((*upper << 4) | *lower); + } + + return uuid; +} + +std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) { + const auto length = uuid_string.length(); + + if (length == 0) { + return {}; + } + + // Check if the input string contains 32 hexadecimal characters. + if (length == RawStringSize) { + return ConstructFromRawString(uuid_string); + } + + // Check if the input string has the length of a RFC 4122 formatted UUID string. + if (length == FormattedStringSize) { + return ConstructFromFormattedString(uuid_string); } - return u128{lo, hi}; + + ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length); + + return {}; +} + +} // Anonymous namespace + +UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {} + +std::string UUID::RawString() const { + return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}" + "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], + uuid[15]); +} + +std::string UUID::FormattedString() const { + return fmt::format("{:02x}{:02x}{:02x}{:02x}" + "-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-" + "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], + uuid[15]); +} + +size_t UUID::Hash() const noexcept { + u64 upper_hash; + u64 lower_hash; + + std::memcpy(&upper_hash, uuid.data(), sizeof(u64)); + std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64)); + + return upper_hash ^ std::rotl(lower_hash, 1); } -UUID UUID::Generate() { +u128 UUID::AsU128() const { + u128 uuid_old; + std::memcpy(&uuid_old, uuid.data(), sizeof(UUID)); + return uuid_old; +} + +UUID UUID::MakeRandom() { std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); - return UUID{distribution(gen), distribution(gen)}; + + return MakeRandomWithSeed(device()); } -std::string UUID::Format() const { - return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]); +UUID UUID::MakeRandomWithSeed(u32 seed) { + // Create and initialize our RNG. + TinyMT rng; + rng.Initialize(seed); + + UUID uuid; + + // Populate the UUID with random bytes. + rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID)); + + return uuid; } -std::string UUID::FormatSwitch() const { - std::array<u8, 16> s{}; - std::memcpy(s.data(), uuid.data(), sizeof(u128)); - return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" - ":02x}{:02x}{:02x}{:02x}{:02x}", - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], - s[12], s[13], s[14], s[15]); +UUID UUID::MakeRandomRFC4122V4() { + auto uuid = MakeRandom(); + + // According to Proposed Standard RFC 4122 Section 4.4, we must: + + // 1. Set the two most significant bits (bits 6 and 7) of the + // clock_seq_hi_and_reserved to zero and one, respectively. + uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); + + // 2. Set the four most significant bits (bits 12 through 15) of the + // time_hi_and_version field to the 4-bit version number from Section 4.1.3. + uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF); + + return uuid; } } // namespace Common diff --git a/src/common/uuid.h b/src/common/uuid.h index 8ea01f8da..fe31e64e6 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -1,9 +1,11 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2022 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once +#include <array> +#include <functional> #include <string> #include <string_view> @@ -11,69 +13,119 @@ namespace Common { -constexpr u128 INVALID_UUID{{0, 0}}; - -/** - * Converts a hex string to a 128-bit unsigned integer. - * - * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix. - * - * This function will assert and return INVALID_UUID under the following conditions: - * - If the hex string is more than 32 characters long - * - If the hex string contains non-hexadecimal characters - * - * @param hex_string Hexadecimal string - * - * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise. - */ -[[nodiscard]] u128 HexStringToU128(std::string_view hex_string); - struct UUID { - // UUIDs which are 0 are considered invalid! - u128 uuid; - UUID() = default; - constexpr explicit UUID(const u128& id) : uuid{id} {} - constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} - explicit UUID(std::string_view hex_string) { - uuid = HexStringToU128(hex_string); - } - - [[nodiscard]] constexpr explicit operator bool() const { - return uuid != INVALID_UUID; - } - - [[nodiscard]] constexpr bool operator==(const UUID& rhs) const { - return uuid == rhs.uuid; - } - - [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const { - return !operator==(rhs); - } - - // TODO(ogniK): Properly generate uuids based on RFC-4122 - [[nodiscard]] static UUID Generate(); - - // Set the UUID to {0,0} to be considered an invalid user - constexpr void Invalidate() { - uuid = INVALID_UUID; + std::array<u8, 0x10> uuid{}; + + /// Constructs an invalid UUID. + constexpr UUID() = default; + + /// Constructs a UUID from a reference to a 128 bit array. + constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {} + + /** + * Constructs a UUID from either: + * 1. A 32 hexadecimal character string representing the bytes of the UUID + * 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * + * The input string may contain uppercase or lowercase characters, but they must: + * 1. Contain valid hexadecimal characters (0-9, a-f, A-F) + * 2. Not contain the "0x" hexadecimal prefix + * + * Should the input string not meet the above requirements, + * an assert will be triggered and an invalid UUID is set instead. + */ + explicit UUID(std::string_view uuid_string); + + ~UUID() = default; + + constexpr UUID(const UUID&) noexcept = default; + constexpr UUID(UUID&&) noexcept = default; + + constexpr UUID& operator=(const UUID&) noexcept = default; + constexpr UUID& operator=(UUID&&) noexcept = default; + + /** + * Returns whether the stored UUID is valid or not. + * + * @returns True if the stored UUID is valid, false otherwise. + */ + constexpr bool IsValid() const { + return uuid != std::array<u8, 0x10>{}; } - [[nodiscard]] constexpr bool IsInvalid() const { - return uuid == INVALID_UUID; - } - [[nodiscard]] constexpr bool IsValid() const { - return !IsInvalid(); + /** + * Returns whether the stored UUID is invalid or not. + * + * @returns True if the stored UUID is invalid, false otherwise. + */ + constexpr bool IsInvalid() const { + return !IsValid(); } - // TODO(ogniK): Properly generate a Nintendo ID - [[nodiscard]] constexpr u64 GetNintendoID() const { - return uuid[0]; + /** + * Returns a 32 hexadecimal character string representing the bytes of the UUID. + * + * @returns A 32 hexadecimal character string of the UUID. + */ + std::string RawString() const; + + /** + * Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. + * + * @returns A RFC 4122 formatted UUID string. + */ + std::string FormattedString() const; + + /** + * Returns a 64-bit hash of the UUID for use in hash table data structures. + * + * @returns A 64-bit hash of the UUID. + */ + size_t Hash() const noexcept; + + /// DO NOT USE. Copies the contents of the UUID into a u128. + u128 AsU128() const; + + /** + * Creates a default UUID "yuzu Default UID". + * + * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID". + */ + static constexpr UUID MakeDefault() { + return UUID{ + {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'}, + }; } - [[nodiscard]] std::string Format() const; - [[nodiscard]] std::string FormatSwitch() const; + /** + * Creates a random UUID. + * + * @returns A random UUID. + */ + static UUID MakeRandom(); + + /** + * Creates a random UUID with a seed. + * + * @param seed A seed to initialize the Mersenne-Twister RNG + * + * @returns A random UUID. + */ + static UUID MakeRandomWithSeed(u32 seed); + + /** + * Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant. + * + * @returns A random UUID that is RFC 4122 Version 4 compliant. + */ + static UUID MakeRandomRFC4122V4(); + + friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; }; -static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); +static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size."); + +/// An invalid UUID. This UUID has all its bytes set to 0. +constexpr UUID InvalidUUID = {}; } // namespace Common @@ -82,7 +134,7 @@ namespace std { template <> struct hash<Common::UUID> { size_t operator()(const Common::UUID& uuid) const noexcept { - return uuid.uuid[1] ^ uuid.uuid[0]; + return uuid.Hash(); } }; diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index ffa282e88..9acf7551e 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -65,16 +65,20 @@ private: #ifdef ARCHITECTURE_x86_64 -std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, - u32 emulated_clock_frequency) { +std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, + u64 emulated_clock_frequency) { const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { rtsc_frequency = EstimateRDTSCFrequency(); } - // Fallback to StandardWallClock if rtsc period is higher than a nano second - if (rtsc_frequency <= 1000000000) { + // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: + // - A nanosecond + // - The emulated CPU frequency + // - The emulated clock counter frequency (CNTFRQ) + if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency || + rtsc_frequency <= emulated_clock_frequency) { return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); } else { @@ -85,8 +89,8 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, #else -std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, - u32 emulated_clock_frequency) { +std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, + u64 emulated_clock_frequency) { return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); } diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index cef3e9499..874448c27 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -13,6 +13,10 @@ namespace Common { class WallClock { public: + static constexpr u64 NS_RATIO = 1'000'000'000; + static constexpr u64 US_RATIO = 1'000'000; + static constexpr u64 MS_RATIO = 1'000; + virtual ~WallClock() = default; /// Returns current wall time in nanoseconds @@ -49,7 +53,7 @@ private: bool is_native; }; -[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, - u32 emulated_clock_frequency); +[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, + u64 emulated_clock_frequency); } // namespace Common diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index fbeacc7e2..d81edb140 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -1,8 +1,12 @@ -// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator +// Project Licensed under GPLv2 or any later version Refer to the license.txt file included. +#include <array> #include <cstring> +#include <iterator> +#include <span> +#include <string_view> +#include "common/bit_util.h" #include "common/common_types.h" #include "common/x64/cpu_detect.h" @@ -17,7 +21,7 @@ // clang-format on #endif -static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { +static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) { #if defined(__DragonFly__) || defined(__FreeBSD__) // Despite the name, this is just do_cpuid() with ECX as second input. cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); @@ -30,7 +34,7 @@ static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { #endif } -static inline void __cpuid(int info[4], int function_id) { +static inline void __cpuid(int info[4], u32 function_id) { return __cpuidex(info, function_id, 0); } @@ -45,6 +49,17 @@ static inline u64 _xgetbv(u32 index) { namespace Common { +CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) { + if (brand_string == "GenuineIntel") { + return Manufacturer::Intel; + } else if (brand_string == "AuthenticAMD") { + return Manufacturer::AMD; + } else if (brand_string == "HygonGenuine") { + return Manufacturer::Hygon; + } + return Manufacturer::Unknown; +} + // Detects the various CPU features static CPUCaps Detect() { CPUCaps caps = {}; @@ -53,75 +68,74 @@ static CPUCaps Detect() { // yuzu at all anyway int cpu_id[4]; - memset(caps.brand_string, 0, sizeof(caps.brand_string)); - // Detect CPU's CPUID capabilities and grab CPU string + // Detect CPU's CPUID capabilities and grab manufacturer string __cpuid(cpu_id, 0x00000000); - u32 max_std_fn = cpu_id[0]; // EAX - - std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); - std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); - std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); - if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69) - caps.manufacturer = Manufacturer::Intel; - else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65) - caps.manufacturer = Manufacturer::AMD; - else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e) - caps.manufacturer = Manufacturer::Hygon; - else - caps.manufacturer = Manufacturer::Unknown; + const u32 max_std_fn = cpu_id[0]; // EAX - __cpuid(cpu_id, 0x80000000); + std::memset(caps.brand_string, 0, std::size(caps.brand_string)); + std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32)); + std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32)); + std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32)); + + caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string); - u32 max_ex_fn = cpu_id[0]; + // Set reasonable default cpu string even if brand string not available + std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string)); - // Set reasonable default brand string even if brand string not available - strcpy(caps.cpu_string, caps.brand_string); + __cpuid(cpu_id, 0x80000000); + + const u32 max_ex_fn = cpu_id[0]; // Detect family and other miscellaneous features if (max_std_fn >= 1) { __cpuid(cpu_id, 0x00000001); - if ((cpu_id[3] >> 25) & 1) - caps.sse = true; - if ((cpu_id[3] >> 26) & 1) - caps.sse2 = true; - if ((cpu_id[2]) & 1) - caps.sse3 = true; - if ((cpu_id[2] >> 9) & 1) - caps.ssse3 = true; - if ((cpu_id[2] >> 19) & 1) - caps.sse4_1 = true; - if ((cpu_id[2] >> 20) & 1) - caps.sse4_2 = true; - if ((cpu_id[2] >> 25) & 1) - caps.aes = true; + caps.sse = Common::Bit<25>(cpu_id[3]); + caps.sse2 = Common::Bit<26>(cpu_id[3]); + caps.sse3 = Common::Bit<0>(cpu_id[2]); + caps.pclmulqdq = Common::Bit<1>(cpu_id[2]); + caps.ssse3 = Common::Bit<9>(cpu_id[2]); + caps.sse4_1 = Common::Bit<19>(cpu_id[2]); + caps.sse4_2 = Common::Bit<20>(cpu_id[2]); + caps.movbe = Common::Bit<22>(cpu_id[2]); + caps.popcnt = Common::Bit<23>(cpu_id[2]); + caps.aes = Common::Bit<25>(cpu_id[2]); + caps.f16c = Common::Bit<29>(cpu_id[2]); // AVX support requires 3 separate checks: // - Is the AVX bit set in CPUID? // - Is the XSAVE bit set in CPUID? // - XGETBV result has the XCR bit set. - if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { + if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { caps.avx = true; - if ((cpu_id[2] >> 12) & 1) + if (Common::Bit<12>(cpu_id[2])) caps.fma = true; } } if (max_std_fn >= 7) { __cpuidex(cpu_id, 0x00000007, 0x00000000); - // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed - if ((cpu_id[1] >> 5) & 1) - caps.avx2 = caps.avx; - if ((cpu_id[1] >> 3) & 1) - caps.bmi1 = true; - if ((cpu_id[1] >> 8) & 1) - caps.bmi2 = true; - // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) - if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && - (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { - caps.avx512 = caps.avx2; + // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed + if (caps.avx) { + caps.avx2 = Common::Bit<5>(cpu_id[1]); + caps.avx512f = Common::Bit<16>(cpu_id[1]); + caps.avx512dq = Common::Bit<17>(cpu_id[1]); + caps.avx512cd = Common::Bit<28>(cpu_id[1]); + caps.avx512bw = Common::Bit<30>(cpu_id[1]); + caps.avx512vl = Common::Bit<31>(cpu_id[1]); + caps.avx512vbmi = Common::Bit<1>(cpu_id[2]); + caps.avx512bitalg = Common::Bit<12>(cpu_id[2]); } + + caps.bmi1 = Common::Bit<3>(cpu_id[1]); + caps.bmi2 = Common::Bit<8>(cpu_id[1]); + caps.sha = Common::Bit<29>(cpu_id[1]); + + caps.gfni = Common::Bit<8>(cpu_id[2]); + + __cpuidex(cpu_id, 0x00000007, 0x00000001); + caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]); } } @@ -138,15 +152,13 @@ static CPUCaps Detect() { if (max_ex_fn >= 0x80000001) { // Check for more features __cpuid(cpu_id, 0x80000001); - if ((cpu_id[2] >> 16) & 1) - caps.fma4 = true; + caps.lzcnt = Common::Bit<5>(cpu_id[2]); + caps.fma4 = Common::Bit<16>(cpu_id[2]); } if (max_ex_fn >= 0x80000007) { __cpuid(cpu_id, 0x80000007); - if (cpu_id[3] & (1 << 8)) { - caps.invariant_tsc = true; - } + caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); } if (max_std_fn >= 0x16) { diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index e3b63302e..40c48b132 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -1,42 +1,65 @@ -// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator +// Project Project Licensed under GPLv2 or any later version Refer to the license.txt file included. #pragma once -namespace Common { +#include <string_view> +#include "common/common_types.h" -enum class Manufacturer : u32 { - Intel = 0, - AMD = 1, - Hygon = 2, - Unknown = 3, -}; +namespace Common { /// x86/x64 CPU capabilities that may be detected by this module struct CPUCaps { + + enum class Manufacturer : u8 { + Unknown = 0, + Intel = 1, + AMD = 2, + Hygon = 3, + }; + + static Manufacturer ParseManufacturer(std::string_view brand_string); + Manufacturer manufacturer; - char cpu_string[0x21]; - char brand_string[0x41]; - bool sse; - bool sse2; - bool sse3; - bool ssse3; - bool sse4_1; - bool sse4_2; - bool lzcnt; - bool avx; - bool avx2; - bool avx512; - bool bmi1; - bool bmi2; - bool fma; - bool fma4; - bool aes; - bool invariant_tsc; + char brand_string[13]; + + char cpu_string[48]; + u32 base_frequency; u32 max_frequency; u32 bus_frequency; + + bool sse : 1; + bool sse2 : 1; + bool sse3 : 1; + bool ssse3 : 1; + bool sse4_1 : 1; + bool sse4_2 : 1; + + bool avx : 1; + bool avx_vnni : 1; + bool avx2 : 1; + bool avx512f : 1; + bool avx512dq : 1; + bool avx512cd : 1; + bool avx512bw : 1; + bool avx512vl : 1; + bool avx512vbmi : 1; + bool avx512bitalg : 1; + + bool aes : 1; + bool bmi1 : 1; + bool bmi2 : 1; + bool f16c : 1; + bool fma : 1; + bool fma4 : 1; + bool gfni : 1; + bool invariant_tsc : 1; + bool lzcnt : 1; + bool movbe : 1; + bool pclmulqdq : 1; + bool popcnt : 1; + bool sha : 1; }; /** diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 82ee2c8a1..91b842829 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -47,9 +47,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen _mm_mfence(); time_point.inner.last_measure = __rdtsc(); time_point.inner.accumulated_ticks = 0U; - ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency); - us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency); - ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency); + ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); + us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); + ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency); clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); } |