// Copyright 2014 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2014 Tony Wasserka // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the owner nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include #include #include #include #include #include "Common/Inline.h" /* * Abstract bitfield class * * Allows endianness-independent access to individual bitfields within some raw * integer value. The assembly generated by this class is identical to the * usage of raw bitfields, so it's a perfectly fine replacement. * * For BitField, X is the distance of the bitfield to the LSB of the * raw value, Y is the length in bits of the bitfield. Z is an integer type * which determines the sign of the bitfield. Z must have the same size as the * raw integer. * * * General usage: * * Create a new union with the raw integer value as a member. * Then for each bitfield you want to expose, add a BitField member * in the union. The template parameters are the bit offset and the number * of desired bits. * * Changes in the bitfield members will then get reflected in the raw integer * value and vice-versa. * * * Sample usage: * * union SomeRegister * { * u32 hex; * * BitField<0,7,u32> first_seven_bits; // unsigned * BitField<7,8,u32> next_eight_bits; // unsigned * BitField<3,15,s32> some_signed_fields; // signed * }; * * This is equivalent to the little-endian specific code: * * union SomeRegister * { * u32 hex; * * struct * { * u32 first_seven_bits : 7; * u32 next_eight_bits : 8; * }; * struct * { * u32 : 3; // padding * s32 some_signed_fields : 15; * }; * }; * * * Caveats: * * 1) * BitField provides automatic casting from and to the storage type where * appropriate. However, when using non-typesafe functions like printf, an * explicit cast must be performed on the BitField object to make sure it gets * passed correctly, e.g.: * printf("Value: %d", (s32)some_register.some_signed_fields); * Note that this does not apply when using fmt, as a formatter is provided that * handles this conversion automatically. * * 2) * Not really a caveat, but potentially irritating: This class is used in some * packed structures that do not guarantee proper alignment. Therefore we have * to use #pragma pack here not to pack the members of the class, but instead * to break GCC's assumption that the members of the class are aligned on * sizeof(StorageType). * TODO(neobrain): Confirm that this is a proper fix and not just masking * symptoms. */ #pragma pack(1) template ::type directly. typename StorageType = typename std::conditional_t< std::is_enum::value, std::underlying_type, std::enable_if>::type> struct BitField { private: // This constructor might be considered ambiguous: // Would it initialize the storage or just the bitfield? // Hence, delete it. Use the assignment operator to set bitfield values! BitField(T val) = delete; public: // Force default constructor to be created // so that we can use this within unions constexpr BitField() = default; // We explicitly delete the copy assignment operator here, because the // default copy assignment would copy the full storage value, rather than // just the bits relevant to this particular bit field. // Ideally, we would just implement the copy assignment to copy only the // relevant bits, but we're prevented from doing that because the savestate // code expects that this class is trivially copyable. BitField& operator=(const BitField&) = delete; DOLPHIN_FORCE_INLINE BitField& operator=(T val) { storage = (storage & ~GetMask()) | ((static_cast(val) << position) & GetMask()); return *this; } constexpr T Value() const { return Value(std::is_signed()); } constexpr operator T() const { return Value(); } static constexpr bool IsSigned() { return std::is_signed(); } static constexpr std::size_t StartBit() { return position; } static constexpr std::size_t NumBits() { return bits; } private: // Unsigned version of StorageType using StorageTypeU = std::make_unsigned_t; constexpr T Value(std::true_type) const { const size_t shift_amount = 8 * sizeof(StorageType) - bits; return static_cast((storage << (shift_amount - position)) >> shift_amount); } constexpr T Value(std::false_type) const { return static_cast((storage & GetMask()) >> position); } static constexpr StorageType GetMask() { return (std::numeric_limits::max() >> (8 * sizeof(StorageType) - bits)) << position; } StorageType storage; static_assert(bits + position <= 8 * sizeof(StorageType), "Bitfield out of range"); static_assert(sizeof(T) <= sizeof(StorageType), "T must fit in StorageType"); // And, you know, just in case people specify something stupid like bits=position=0x80000000 static_assert(position < 8 * sizeof(StorageType), "Invalid position"); static_assert(bits <= 8 * sizeof(T), "Invalid number of bits"); static_assert(bits > 0, "Invalid number of bits"); }; #pragma pack() // Use the underlying type's formatter for BitFields, if one exists template struct fmt::formatter> { fmt::formatter m_formatter; constexpr auto parse(format_parse_context& ctx) { return m_formatter.parse(ctx); } template auto format(const BitField& bitfield, FormatContext& ctx) { return m_formatter.format(bitfield.Value(), ctx); } }; // Language limitations require the following to make these formattable // (formatter::Ref> is not legal) template class BitFieldArrayConstRef; template class BitFieldArrayRef; template class BitFieldArrayConstIterator; template class BitFieldArrayIterator; #pragma pack(1) template ::type directly. typename StorageType = typename std::conditional_t< std::is_enum::value, std::underlying_type, std::enable_if>::type> struct BitFieldArray { using Ref = BitFieldArrayRef; using ConstRef = BitFieldArrayConstRef; using Iterator = BitFieldArrayIterator; using ConstIterator = BitFieldArrayConstIterator; private: // This constructor might be considered ambiguous: // Would it initialize the storage or just the bitfield? // Hence, delete it. Use the assignment operator to set bitfield values! BitFieldArray(T val) = delete; public: // Force default constructor to be created // so that we can use this within unions constexpr BitFieldArray() = default; // We explicitly delete the copy assignment operator here, because the // default copy assignment would copy the full storage value, rather than // just the bits relevant to this particular bit field. // Ideally, we would just implement the copy assignment to copy only the // relevant bits, but we're prevented from doing that because the savestate // code expects that this class is trivially copyable. BitFieldArray& operator=(const BitFieldArray&) = delete; public: constexpr bool IsSigned() const { return std::is_signed(); } constexpr std::size_t StartBit() const { return position; } constexpr std::size_t NumBits() const { return bits; } constexpr std::size_t Size() const { return size; } constexpr std::size_t TotalNumBits() const { return bits * size; } constexpr T Value(size_t index) const { return Value(std::is_signed(), index); } void SetValue(size_t index, T value) { const size_t pos = position + bits * index; storage = (storage & ~GetElementMask(index)) | ((static_cast(value) << pos) & GetElementMask(index)); } Ref operator[](size_t index) { return Ref(this, index); } constexpr const ConstRef operator[](size_t index) const { return ConstRef(this, index); } constexpr Iterator begin() { return Iterator(this, 0); } constexpr Iterator end() { return Iterator(this, size); } constexpr ConstIterator begin() const { return ConstIterator(this, 0); } constexpr ConstIterator end() const { return ConstIterator(this, size); } constexpr ConstIterator cbegin() const { return begin(); } constexpr ConstIterator cend() const { return end(); } private: // Unsigned version of StorageType using StorageTypeU = std::make_unsigned_t; constexpr T Value(std::true_type, size_t index) const { const size_t pos = position + bits * index; const size_t shift_amount = 8 * sizeof(StorageType) - bits; return static_cast((storage << (shift_amount - pos)) >> shift_amount); } constexpr T Value(std::false_type, size_t index) const { const size_t pos = position + bits * index; return static_cast((storage & GetElementMask(index)) >> pos); } static constexpr StorageType GetElementMask(size_t index) { const size_t pos = position + bits * index; return (std::numeric_limits::max() >> (8 * sizeof(StorageType) - bits)) << pos; } StorageType storage; static_assert(bits * size + position <= 8 * sizeof(StorageType), "Bitfield array out of range"); static_assert(sizeof(T) <= sizeof(StorageType), "T must fit in StorageType"); // And, you know, just in case people specify something stupid like bits=position=0x80000000 static_assert(position < 8 * sizeof(StorageType), "Invalid position"); static_assert(bits <= 8 * sizeof(T), "Invalid number of bits"); static_assert(bits > 0, "Invalid number of bits"); static_assert(size <= 8 * sizeof(StorageType), "Invalid size"); static_assert(size > 0, "Invalid size"); }; #pragma pack() template class BitFieldArrayConstRef { friend struct BitFieldArray; friend class BitFieldArrayConstIterator; public: constexpr T Value() const { return m_array->Value(m_index); }; constexpr operator T() const { return Value(); } private: constexpr BitFieldArrayConstRef(const BitFieldArray* array, size_t index) : m_array(array), m_index(index) { } const BitFieldArray* const m_array; const size_t m_index; }; template class BitFieldArrayRef { friend struct BitFieldArray; friend class BitFieldArrayIterator; public: constexpr T Value() const { return m_array->Value(m_index); }; constexpr operator T() const { return Value(); } T operator=(const BitFieldArrayRef& value) const { m_array->SetValue(m_index, value); return value; } T operator=(T value) const { m_array->SetValue(m_index, value); return value; } private: constexpr BitFieldArrayRef(BitFieldArray* array, size_t index) : m_array(array), m_index(index) { } BitFieldArray* const m_array; const size_t m_index; }; // Satisfies LegacyOutputIterator / std::output_iterator. // Does not satisfy LegacyInputIterator / std::input_iterator as std::output_iterator_tag does not // extend std::input_iterator_tag. // Does not satisfy LegacyForwardIterator / std::forward_iterator, as that requires use of real // references instead of proxy objects. // This iterator allows use of BitFieldArray in range-based for loops, and with fmt::join. template class BitFieldArrayIterator { friend struct BitFieldArray; public: using iterator_category = std::output_iterator_tag; using value_type = T; using difference_type = ptrdiff_t; using pointer = void; using reference = BitFieldArrayRef; private: constexpr BitFieldArrayIterator(BitFieldArray* array, size_t index) : m_array(array), m_index(index) { } public: // Required by std::input_or_output_iterator constexpr BitFieldArrayIterator() = default; // Required by LegacyIterator constexpr BitFieldArrayIterator(const BitFieldArrayIterator& other) = default; // Required by LegacyIterator BitFieldArrayIterator& operator=(const BitFieldArrayIterator& other) = default; // Move constructor and assignment operators, explicitly defined for completeness constexpr BitFieldArrayIterator(BitFieldArrayIterator&& other) = default; BitFieldArrayIterator& operator=(BitFieldArrayIterator&& other) = default; public: BitFieldArrayIterator& operator++() { m_index++; return *this; } BitFieldArrayIterator operator++(int) { BitFieldArrayIterator other(*this); ++*this; return other; } constexpr reference operator*() const { return reference(m_array, m_index); } constexpr bool operator==(BitFieldArrayIterator other) const { return m_index == other.m_index; } constexpr bool operator!=(BitFieldArrayIterator other) const { return m_index != other.m_index; } private: BitFieldArray* m_array; size_t m_index; }; // Satisfies LegacyInputIterator / std::input_iterator. // Does not satisfy LegacyForwardIterator / std::forward_iterator, as that requires use of real // references instead of proxy objects. // This iterator allows use of BitFieldArray in range-based for loops, and with fmt::join. template class BitFieldArrayConstIterator { friend struct BitFieldArray; public: using iterator_category = std::input_iterator_tag; using value_type = T; using difference_type = ptrdiff_t; using pointer = void; using reference = BitFieldArrayConstRef; private: constexpr BitFieldArrayConstIterator(const BitFieldArray* array, size_t index) : m_array(array), m_index(index) { } public: // Required by std::input_or_output_iterator constexpr BitFieldArrayConstIterator() = default; // Required by LegacyIterator constexpr BitFieldArrayConstIterator(const BitFieldArrayConstIterator& other) = default; // Required by LegacyIterator BitFieldArrayConstIterator& operator=(const BitFieldArrayConstIterator& other) = default; // Move constructor and assignment operators, explicitly defined for completeness constexpr BitFieldArrayConstIterator(BitFieldArrayConstIterator&& other) = default; BitFieldArrayConstIterator& operator=(BitFieldArrayConstIterator&& other) = default; public: BitFieldArrayConstIterator& operator++() { m_index++; return *this; } BitFieldArrayConstIterator operator++(int) { BitFieldArrayConstIterator other(*this); ++*this; return other; } constexpr reference operator*() const { return reference(m_array, m_index); } constexpr bool operator==(BitFieldArrayConstIterator other) const { return m_index == other.m_index; } constexpr bool operator!=(BitFieldArrayConstIterator other) const { return m_index != other.m_index; } private: const BitFieldArray* m_array; size_t m_index; }; template struct fmt::formatter> { fmt::formatter m_formatter; constexpr auto parse(format_parse_context& ctx) { return m_formatter.parse(ctx); } template auto format(const BitFieldArrayRef& ref, FormatContext& ctx) { return m_formatter.format(ref.Value(), ctx); } }; template struct fmt::formatter> { fmt::formatter m_formatter; constexpr auto parse(format_parse_context& ctx) { return m_formatter.parse(ctx); } template auto format(const BitFieldArrayConstRef& ref, FormatContext& ctx) { return m_formatter.format(ref.Value(), ctx); } };