common: Introduce macro-based BitField

The current BitField implementation requires the use of the union, which makes it both non-constexpr (due to accessing inactive union members), and prevents the usage of smaller types and enums, reducing its flexibility.

Furthermore, BitField is not trivially copyable (and by extension, not trivial).

Moreover, it is not well documented that BitField performs automatic sign extension on signed integers, as a recent PR made an assumption that these signed integers were zero extended.

These considerations resulted in this new BitField implementation, which uses macros to generate functions for accessing / setting bitfield values.

2 macros are provided:

`YUZU_RO_BITFIELD` for read-only BitFields, which generates a member function that directly returns the specified type.

`YUZU_BITFIELD`, which generates a member function that returns a `BitFieldHelper` class which allows setting the value using `operator=` or `Set()`, and performs implicit conversion to the specified type.

Types:

The following type tags are added:

Automatic type deduction:
AutoUInt, AutoSignExtSInt, AutoZeroExtSInt, AutoFloat

Automatic type deduction selects the smallest type that contains at least the specified NumBits bits.
However, in testing, MSVC is pretty bad at optimizing smaller-than-32-bit accesses, so it may be advisable to specify 32 bit types in performance sensitive code.

Signed Integers:
SignExtSInt<>, ZeroExtSInt<>

The new SignExtSInt and ZeroExtSInt are mandatory type tags for signed integers to explicitly specify whether to sign extend (preserving the sign and value) or zero extend the bitfield value.

These changes have sacrificed compatibility with `_be` types. This is a worthwhile sacrifice as we do not have usages of these types in BitField.

Example usage:

```cpp
struct MyTestStruct {
    u32 raw;

    YUZU_RO_BITFIELD(0, 3, AutoUInt, field0);
    YUZU_BITFIELD(3, 4, AutoSignExtSInt, field1);
    YUZU_BITFIELD(7, 5, ZeroExtSInt<s16>, field2);
    YUZU_RO_BITFIELD(12, 1, bool, flag0);
};

MyTestStruct s{0x1234567};
s.field1() = -1;
s.field2() = -2;
fmt::print("field0={}, field1={}, field2={}, flag0={}", s.field0(), s.field1(), s.field2(), s.flag0());
```
This commit is contained in:
Morph 2023-07-11 01:42:15 -04:00
parent 18000df5e9
commit 334567d33b
4 changed files with 616 additions and 0 deletions

View File

@ -90,6 +90,7 @@ add_library(common STATIC
microprofileui.h
multi_level_page_table.cpp
multi_level_page_table.h
new_bit_field.h
nvidia_flags.cpp
nvidia_flags.h
overflow.h

398
src/common/new_bit_field.h Normal file
View File

@ -0,0 +1,398 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <fmt/core.h>
#include "common/bit_cast.h"
// Forward declarations
struct AutoUInt;
struct AutoSignExtSInt;
struct AutoZeroExtSInt;
struct AutoFloat;
template <typename T>
struct SignExtSInt;
template <typename T>
struct ZeroExtSInt;
namespace Common::BitField {
template <typename T>
constexpr size_t BitSize() {
return sizeof(T) * 8;
}
template <typename T>
concept IsPOD = std::is_trivial_v<T> && std::is_standard_layout_v<T>;
template <typename T, typename = typename std::is_enum<T>::type>
struct ToUnderlyingType {
using type = T;
};
template <typename T>
struct ToUnderlyingType<T, std::true_type> {
using type = std::underlying_type_t<T>;
};
// clang-format off
// Automatically deduces the smallest unsigned integer type that contains at least NumBits bits.
template <size_t NumBits>
using SmallestUIntType = std::conditional_t<NumBits <= BitSize<std::uint8_t>(), std::uint8_t,
std::conditional_t<NumBits <= BitSize<std::uint16_t>(), std::uint16_t,
std::conditional_t<NumBits <= BitSize<std::uint32_t>(), std::uint32_t,
std::conditional_t<NumBits <= BitSize<std::uint64_t>(), std::uint64_t, std::uint64_t>>>>;
// Automatically deduces the smallest signed integer type that contains at least NumBits bits.
template <size_t NumBits>
using SmallestSIntType = std::conditional_t<NumBits <= BitSize<std::int8_t>(), std::int8_t,
std::conditional_t<NumBits <= BitSize<std::int16_t>(), std::int16_t,
std::conditional_t<NumBits <= BitSize<std::int32_t>(), std::int32_t,
std::conditional_t<NumBits <= BitSize<std::int64_t>(), std::int64_t, std::int64_t>>>>;
// Automatically deduces the smallest floating point type that contains at exactly NumBits bits.
template <size_t NumBits>
using SmallestFloatType = std::conditional_t<NumBits == BitSize<float>(), float,
std::conditional_t<NumBits == BitSize<double>(), double, double>>;
// clang-format on
struct TagType {};
struct AutoType : public TagType {};
struct SIntType : public TagType {};
template <typename T, size_t NumBits,
typename = typename std::is_base_of<Common::BitField::AutoType, T>::type>
struct DeduceAutoType {
using type = void;
};
template <typename T, size_t NumBits>
struct DeduceAutoType<T, NumBits, std::true_type> {
// clang-format off
using type = std::conditional_t<std::is_same_v<T, AutoUInt>, Common::BitField::SmallestUIntType<NumBits>,
std::conditional_t<std::is_same_v<T, AutoSignExtSInt>, Common::BitField::SmallestSIntType<NumBits>,
std::conditional_t<std::is_same_v<T, AutoZeroExtSInt>, Common::BitField::SmallestSIntType<NumBits>,
std::conditional_t<std::is_same_v<T, AutoFloat>, Common::BitField::SmallestFloatType<NumBits>, void>>>>;
// clang-format on
};
template <typename T, typename = typename std::is_base_of<Common::BitField::SIntType, T>::type>
struct DeduceSIntType {
using type = void;
};
template <typename T>
struct DeduceSIntType<T, std::true_type> {
using type = typename T::OutputType;
};
template <typename T, typename = typename std::is_base_of<Common::BitField::TagType, T>::type>
struct DeduceSignExtendValue {
static constexpr bool value = false;
};
template <typename T>
struct DeduceSignExtendValue<T, std::true_type> {
static constexpr bool value = T::SignExtend;
};
template <typename T, size_t NumBits>
struct TypeTraits {
static_assert(NumBits != 0, "NumBits must not be 0.");
static_assert(!std::is_same_v<T, bool> || NumBits == 1, "For bool, NumBits must be exactly 1.");
static_assert(!std::signed_integral<typename ToUnderlyingType<T>::type>,
"For signed integers, use SignExtSInt<Type> or ZeroExtSInt<Type> instead.");
using AutoType = typename DeduceAutoType<T, NumBits>::type;
using SIntType = typename DeduceSIntType<T>::type;
// clang-format off
using OutputType = std::conditional_t<!std::is_void_v<AutoType>, AutoType,
std::conditional_t<!std::is_void_v<SIntType>, SIntType,
T>>;
// clang-format on
static_assert(IsPOD<OutputType>, "Type must be a POD type.");
using UnderlyingType = typename ToUnderlyingType<OutputType>::type;
// For integral types, assert that OutputType contains at least NumBits bits.
static_assert(!std::is_integral_v<UnderlyingType> || BitSize<UnderlyingType>() >= NumBits,
"Type must contain at least NumBits bits.");
// For all other types, assert that OutputType contains exactly NumBits bits.
static_assert(std::is_integral_v<UnderlyingType> || BitSize<UnderlyingType>() == NumBits,
"Type must contain exactly NumBits bits.");
static constexpr bool SignExtend = DeduceSignExtendValue<T>::value;
};
template <typename To, typename From>
constexpr To AutoBitCast(const From& from) {
if constexpr (sizeof(To) != sizeof(From)) {
using FromUnsignedType = SmallestUIntType<BitSize<From>()>;
using ToUnsignedType = SmallestUIntType<BitSize<To>()>;
return BitCast<To>(static_cast<ToUnsignedType>(BitCast<FromUnsignedType>(from)));
} else {
return BitCast<To>(from);
}
}
template <typename UnsignedRawType, size_t Position, size_t NumBits>
constexpr UnsignedRawType GenerateBitMask() {
static_assert(std::unsigned_integral<UnsignedRawType>);
if constexpr (BitSize<UnsignedRawType>() == NumBits) {
return ~UnsignedRawType{0};
} else {
return ((UnsignedRawType{1} << NumBits) - 1) << Position;
}
}
template <typename RawType, typename OutputType, size_t Position, size_t NumBits>
constexpr void InsertBits(RawType& raw, OutputType value) {
using UnsignedType = SmallestUIntType<BitSize<RawType>()>;
static_assert(sizeof(RawType) == sizeof(UnsignedType));
constexpr auto Mask = GenerateBitMask<UnsignedType, Position, NumBits>();
raw = AutoBitCast<RawType>((AutoBitCast<UnsignedType>(raw) & ~Mask) |
((AutoBitCast<UnsignedType>(value) << Position) & Mask));
}
template <typename RawType, typename OutputType, size_t Position, size_t NumBits, bool SignExtend>
constexpr OutputType ExtractBits(const RawType& raw) {
if constexpr (SignExtend) {
using SignedType = SmallestSIntType<BitSize<RawType>()>;
static_assert(sizeof(RawType) == sizeof(SignedType));
constexpr auto RightShift = BitSize<RawType>() - NumBits;
constexpr auto LeftShift = RightShift - Position;
return AutoBitCast<OutputType>((AutoBitCast<SignedType>(raw) << LeftShift) >> RightShift);
} else {
using UnsignedType = SmallestUIntType<BitSize<RawType>()>;
static_assert(sizeof(RawType) == sizeof(UnsignedType));
constexpr auto Mask = GenerateBitMask<UnsignedType, Position, NumBits>();
return AutoBitCast<OutputType>((AutoBitCast<UnsignedType>(raw) & Mask) >> Position);
}
}
template <typename RawType, typename OutputType, size_t Position, size_t NumBits, bool SignExtend>
class BitFieldHelper final {
public:
constexpr BitFieldHelper(RawType& raw_) : raw{raw_} {}
BitFieldHelper(const BitFieldHelper&) = delete;
BitFieldHelper& operator=(const BitFieldHelper&) = delete;
BitFieldHelper(BitFieldHelper&&) = delete;
BitFieldHelper& operator=(BitFieldHelper&&) = delete;
constexpr void Set(OutputType value) noexcept {
return InsertBits<RawType, OutputType, Position, NumBits>(raw, value);
}
constexpr BitFieldHelper& operator=(OutputType value) noexcept {
Set(value);
return *this;
}
[[nodiscard]] constexpr OutputType Get() const noexcept {
return ExtractBits<RawType, OutputType, Position, NumBits, SignExtend>(raw);
}
[[nodiscard]] constexpr operator OutputType() const noexcept {
return Get();
}
template <typename Other>
[[nodiscard]] friend constexpr bool operator==(const BitFieldHelper& lhs,
const Other& rhs) noexcept {
return lhs.Get() == rhs;
}
template <typename Other>
[[nodiscard]] friend constexpr bool operator==(const Other& lhs,
const BitFieldHelper& rhs) noexcept {
return lhs == rhs.Get();
}
[[nodiscard]] constexpr bool operator==(const BitFieldHelper& other) const noexcept {
return Get() == other.Get();
}
private:
RawType& raw;
};
template <typename RawType, typename OutputType, size_t Position, size_t NumBits, bool SignExtend>
inline auto format_as(BitFieldHelper<RawType, OutputType, Position, NumBits, SignExtend> bitfield) {
return bitfield.Get();
}
} // namespace Common::BitField
/**
* A type tag that automatically deduces the smallest unsigned integer type that
* contains at least NumBits bits in the bitfield.
* Currently supported unsigned integer types:
* - u8 (8 bits)
* - u16 (16 bits)
* - u32 (32 bits)
* - u64 (64 bits)
*/
struct AutoUInt : public Common::BitField::AutoType {
static constexpr bool SignExtend = false;
};
/**
* A type tag that automatically deduces the smallest signed integer type that
* contains at least NumBits bits in the bitfield.
* Additionally, the value is treated as a NumBits-bit 2's complement integer
* which is sign-extended to fit in the output type, preserving its sign and value.
* Currently supported signed integer types:
* - s8 (8 bits)
* - s16 (16 bits)
* - s32 (32 bits)
* - s64 (64 bits)
*/
struct AutoSignExtSInt : public Common::BitField::AutoType {
static constexpr bool SignExtend = true;
};
/**
* A type tag that automatically deduces the smallest signed integer type that
* contains at least NumBits bits in the bitfield.
* Unlike AutoSignExtInt, the value is zero-extended to fit in the output type,
* effectively treating it as an unsigned integer that is bitcast to a signed integer.
* Its sign and value are not preserved, unless the output type contains exactly NumBits bits.
* Currently supported signed integer types:
* - s8 (8 bits)
* - s16 (16 bits)
* - s32 (32 bits)
* - s64 (64 bits)
*/
struct AutoZeroExtSInt : public Common::BitField::AutoType {
static constexpr bool SignExtend = false;
};
/**
* A type tag that automatically deduces the smallest floating point type that
* contains exactly NumBits bits in the bitfield.
* Currently supported floating point types:
* - float (32 bits)
* - double (64 bits)
*/
struct AutoFloat : public Common::BitField::AutoType {
static constexpr bool SignExtend = false;
};
/**
* A type tag that treats the value as a NumBits-bit 2's Complement Integer
* which is sign-extended to fit in the output type, preserving its sign and value.
* Currently supported signed integer types:
* - s8 (8 bits)
* - s16 (16 bits)
* - s32 (32 bits)
* - s64 (64 bits)
*/
template <typename T>
struct SignExtSInt : public Common::BitField::SIntType {
static_assert(std::signed_integral<typename Common::BitField::ToUnderlyingType<T>::type>,
"SignExtSInt<Type>: Type must be a signed integral.");
using OutputType = T;
static constexpr bool SignExtend = true;
};
/**
* A type tag that, unlike SignExtInt, zero-extends the value to fit in the output type,
* effectively treating it as an unsigned integer that is bitcast to a signed integer.
* Its sign and value are not preserved, unless the output type contains exactly NumBits bits.
* Currently supported signed integer types:
* - s8 (8 bits)
* - s16 (16 bits)
* - s32 (32 bits)
* - s64 (64 bits)
*/
template <typename T>
struct ZeroExtSInt : public Common::BitField::SIntType {
static_assert(std::signed_integral<typename Common::BitField::ToUnderlyingType<T>::type>,
"ZeroExtSInt<Type>: Type must be a signed integral.");
using OutputType = T;
static constexpr bool SignExtend = false;
};
template <typename T, size_t NumBits>
using BitFieldOutputType = typename Common::BitField::TypeTraits<T, NumBits>::OutputType;
#define YUZU_RO_BITFIELD(Position, NumBits, Type, Name) \
constexpr BitFieldOutputType<Type, NumBits> Name() const { \
using BitFieldTypeTraits = Common::BitField::TypeTraits<Type, NumBits>; \
using OutputType = BitFieldTypeTraits::OutputType; \
constexpr bool SignExtend = BitFieldTypeTraits::SignExtend; \
using ThisType = std::remove_cvref_t<decltype(*this)>; \
static_assert(!std::is_union_v<ThisType>, \
"An object containing BitFields cannot be a union type."); \
static_assert(Common::BitField::IsPOD<ThisType>, \
"An object containing BitFields must be a POD type."); \
static_assert(Common::BitField::BitSize<ThisType>() <= 64, \
"An object containing BitFields must be at most 64 bits in size."); \
/* A structured binding is used to decompose *this into its constituent members. */ \
/* It also allows us to guarantee that we only have one member in *this object. */ \
/* Bit manipulation is performed on this member, so it must support bit operators. */ \
const auto& [yuzu_raw_value] = *this; \
using RawType = std::remove_cvref_t<decltype(raw)>; \
static_assert(Common::BitField::IsPOD<RawType>, \
"An object containing BitFields must be a POD type."); \
static_assert(Common::BitField::BitSize<RawType>() <= 64, \
"An object containing BitFields must be at most 64 bits in size."); \
static_assert(Position < Common::BitField::BitSize<RawType>(), \
"BitField is out of range."); \
static_assert(NumBits <= Common::BitField::BitSize<RawType>(), \
"BitField is out of range."); \
static_assert(Position + NumBits <= Common::BitField::BitSize<RawType>(), \
"BitField is out of range."); \
return Common::BitField::ExtractBits<RawType, OutputType, Position, NumBits, SignExtend>( \
yuzu_raw_value); \
}
#define YUZU_BITFIELD(Position, NumBits, Type, Name) \
YUZU_RO_BITFIELD(Position, NumBits, Type, Name); \
constexpr auto Name() { \
using BitFieldTypeTraits = Common::BitField::TypeTraits<Type, NumBits>; \
using OutputType = BitFieldTypeTraits::OutputType; \
constexpr bool SignExtend = BitFieldTypeTraits::SignExtend; \
using ThisType = std::remove_cvref_t<decltype(*this)>; \
static_assert(!std::is_union_v<ThisType>, \
"An object containing BitFields cannot be a union type."); \
static_assert(Common::BitField::IsPOD<ThisType>, \
"An object containing BitFields must be a POD type."); \
static_assert(Common::BitField::BitSize<ThisType>() <= 64, \
"An object containing BitFields must be at most 64 bits in size."); \
/* A structured binding is used to decompose *this into its constituent members. */ \
/* It also allows us to guarantee that we only have one member in *this object. */ \
/* Bit manipulation is performed on this member, so it must support bit operators. */ \
auto& [yuzu_raw_value] = *this; \
using RawType = std::remove_cvref_t<decltype(raw)>; \
static_assert(Common::BitField::IsPOD<RawType>, \
"An object containing BitFields must be a POD type."); \
static_assert(Common::BitField::BitSize<RawType>() <= 64, \
"An object containing BitFields must be at most 64 bits in size."); \
static_assert(Position < Common::BitField::BitSize<RawType>(), \
"BitField is out of range."); \
static_assert(NumBits <= Common::BitField::BitSize<RawType>(), \
"BitField is out of range."); \
static_assert(Position + NumBits <= Common::BitField::BitSize<RawType>(), \
"BitField is out of range."); \
return Common::BitField::BitFieldHelper<RawType, OutputType, Position, NumBits, \
SignExtend>{yuzu_raw_value}; \
}

View File

@ -7,6 +7,7 @@ add_executable(tests
common/container_hash.cpp
common/fibers.cpp
common/host_memory.cpp
common/new_bit_field.cpp
common/param_package.cpp
common/range_map.cpp
common/ring_buffer.cpp

View File

@ -0,0 +1,216 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/new_bit_field.h"
namespace Common::BitField {
enum class U8Enum : std::uint8_t {};
enum class U16Enum : std::uint16_t {};
enum class U32Enum : std::uint32_t {};
enum class U64Enum : std::uint64_t {};
enum class S8Enum : std::int8_t {};
enum class S16Enum : std::int16_t {};
enum class S32Enum : std::int32_t {};
enum class S64Enum : std::int64_t {};
template <size_t N>
struct NByteStruct {
std::array<std::uint8_t, N> raw;
};
struct NonTrivialStruct {
NonTrivialStruct() {}
};
struct NonTriviallyCopyableStruct {
NonTriviallyCopyableStruct(const NonTriviallyCopyableStruct&) {}
};
struct NonStandardLayoutStruct {
std::uint8_t a;
private:
std::uint8_t b;
};
struct NonPODStruct {
virtual void Foo() const = 0;
};
// clang-format off
// Tests that must pass.
static_assert(std::is_same_v<bool, BitFieldOutputType<bool, 1>>);
static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<std::uint8_t, 1>>);
static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<std::uint8_t, 8>>);
static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<AutoUInt, 1>>);
static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<AutoUInt, 8>>);
static_assert(std::is_same_v<std::uint16_t, BitFieldOutputType<std::uint16_t, 1>>);
static_assert(std::is_same_v<std::uint16_t, BitFieldOutputType<std::uint16_t, 16>>);
static_assert(std::is_same_v<std::uint16_t, BitFieldOutputType<AutoUInt, 9>>);
static_assert(std::is_same_v<std::uint16_t, BitFieldOutputType<AutoUInt, 16>>);
static_assert(std::is_same_v<std::uint32_t, BitFieldOutputType<std::uint32_t, 1>>);
static_assert(std::is_same_v<std::uint32_t, BitFieldOutputType<std::uint32_t, 32>>);
static_assert(std::is_same_v<std::uint32_t, BitFieldOutputType<AutoUInt, 17>>);
static_assert(std::is_same_v<std::uint32_t, BitFieldOutputType<AutoUInt, 32>>);
static_assert(std::is_same_v<std::uint64_t, BitFieldOutputType<std::uint64_t, 1>>);
static_assert(std::is_same_v<std::uint64_t, BitFieldOutputType<std::uint64_t, 64>>);
static_assert(std::is_same_v<std::uint64_t, BitFieldOutputType<AutoUInt, 33>>);
static_assert(std::is_same_v<std::uint64_t, BitFieldOutputType<AutoUInt, 64>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<AutoSignExtSInt, 1>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<AutoSignExtSInt, 8>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<AutoZeroExtSInt, 1>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<AutoZeroExtSInt, 8>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<SignExtSInt<std::int8_t>, 1>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<SignExtSInt<std::int8_t>, 8>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<ZeroExtSInt<std::int8_t>, 1>>);
static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<ZeroExtSInt<std::int8_t>, 8>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<AutoSignExtSInt, 9>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<AutoSignExtSInt, 16>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<AutoZeroExtSInt, 9>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<AutoZeroExtSInt, 16>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<SignExtSInt<std::int16_t>, 9>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<SignExtSInt<std::int16_t>, 16>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<SignExtSInt<std::int16_t>, 9>>);
static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<SignExtSInt<std::int16_t>, 16>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<AutoSignExtSInt, 17>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<AutoSignExtSInt, 32>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<AutoZeroExtSInt, 17>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<AutoZeroExtSInt, 32>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<SignExtSInt<std::int32_t>, 17>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<SignExtSInt<std::int32_t>, 32>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<SignExtSInt<std::int32_t>, 17>>);
static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<SignExtSInt<std::int32_t>, 32>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<AutoSignExtSInt, 33>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<AutoSignExtSInt, 64>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<AutoZeroExtSInt, 33>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<AutoZeroExtSInt, 64>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<SignExtSInt<std::int64_t>, 33>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<SignExtSInt<std::int64_t>, 64>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<SignExtSInt<std::int64_t>, 33>>);
static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<SignExtSInt<std::int64_t>, 64>>);
static_assert(std::is_same_v<float, BitFieldOutputType<float, 32>>);
static_assert(std::is_same_v<float, BitFieldOutputType<AutoFloat, 32>>);
static_assert(std::is_same_v<double, BitFieldOutputType<double, 64>>);
static_assert(std::is_same_v<double, BitFieldOutputType<AutoFloat, 64>>);
static_assert(std::is_same_v<U8Enum, BitFieldOutputType<U8Enum, 1>>);
static_assert(std::is_same_v<U8Enum, BitFieldOutputType<U8Enum, 8>>);
static_assert(std::is_same_v<U16Enum, BitFieldOutputType<U16Enum, 1>>);
static_assert(std::is_same_v<U16Enum, BitFieldOutputType<U16Enum, 16>>);
static_assert(std::is_same_v<U32Enum, BitFieldOutputType<U32Enum, 1>>);
static_assert(std::is_same_v<U32Enum, BitFieldOutputType<U32Enum, 32>>);
static_assert(std::is_same_v<U64Enum, BitFieldOutputType<U64Enum, 1>>);
static_assert(std::is_same_v<U64Enum, BitFieldOutputType<U64Enum, 64>>);
static_assert(std::is_same_v<S8Enum, BitFieldOutputType<SignExtSInt<S8Enum>, 1>>);
static_assert(std::is_same_v<S8Enum, BitFieldOutputType<SignExtSInt<S8Enum>, 8>>);
static_assert(std::is_same_v<S8Enum, BitFieldOutputType<ZeroExtSInt<S8Enum>, 1>>);
static_assert(std::is_same_v<S8Enum, BitFieldOutputType<ZeroExtSInt<S8Enum>, 8>>);
static_assert(std::is_same_v<S16Enum, BitFieldOutputType<SignExtSInt<S16Enum>, 1>>);
static_assert(std::is_same_v<S16Enum, BitFieldOutputType<SignExtSInt<S16Enum>, 16>>);
static_assert(std::is_same_v<S16Enum, BitFieldOutputType<ZeroExtSInt<S16Enum>, 1>>);
static_assert(std::is_same_v<S16Enum, BitFieldOutputType<ZeroExtSInt<S16Enum>, 16>>);
static_assert(std::is_same_v<S32Enum, BitFieldOutputType<SignExtSInt<S32Enum>, 1>>);
static_assert(std::is_same_v<S32Enum, BitFieldOutputType<SignExtSInt<S32Enum>, 32>>);
static_assert(std::is_same_v<S32Enum, BitFieldOutputType<ZeroExtSInt<S32Enum>, 1>>);
static_assert(std::is_same_v<S32Enum, BitFieldOutputType<ZeroExtSInt<S32Enum>, 32>>);
static_assert(std::is_same_v<S64Enum, BitFieldOutputType<SignExtSInt<S64Enum>, 1>>);
static_assert(std::is_same_v<S64Enum, BitFieldOutputType<SignExtSInt<S64Enum>, 64>>);
static_assert(std::is_same_v<S64Enum, BitFieldOutputType<ZeroExtSInt<S64Enum>, 1>>);
static_assert(std::is_same_v<S64Enum, BitFieldOutputType<ZeroExtSInt<S64Enum>, 64>>);
static_assert(std::is_same_v<NByteStruct<1>, BitFieldOutputType<NByteStruct<1>, 8>>);
static_assert(std::is_same_v<NByteStruct<2>, BitFieldOutputType<NByteStruct<2>, 16>>);
static_assert(std::is_same_v<NByteStruct<3>, BitFieldOutputType<NByteStruct<3>, 24>>);
static_assert(std::is_same_v<NByteStruct<4>, BitFieldOutputType<NByteStruct<4>, 32>>);
static_assert(std::is_same_v<NByteStruct<5>, BitFieldOutputType<NByteStruct<5>, 40>>);
static_assert(std::is_same_v<NByteStruct<6>, BitFieldOutputType<NByteStruct<6>, 48>>);
static_assert(std::is_same_v<NByteStruct<7>, BitFieldOutputType<NByteStruct<7>, 56>>);
static_assert(std::is_same_v<NByteStruct<8>, BitFieldOutputType<NByteStruct<8>, 64>>);
static_assert(TypeTraits<AutoSignExtSInt, 8>::SignExtend == true);
static_assert(TypeTraits<AutoZeroExtSInt, 8>::SignExtend == false);
static_assert(TypeTraits<SignExtSInt<std::int8_t>, 8>::SignExtend == true);
static_assert(TypeTraits<SignExtSInt<S8Enum>, 8>::SignExtend == true);
static_assert(TypeTraits<ZeroExtSInt<std::int8_t>, 8>::SignExtend == false);
static_assert(TypeTraits<ZeroExtSInt<S8Enum>, 8>::SignExtend == false);
// Tests that will fail if uncommented.
// NumBits must not be 0.
// static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<std::uint8_t, 0>>);
// For bool, NumBits must be exactly 1.
// static_assert(std::is_same_v<bool, BitFieldOutputType<bool, 8>>);
// For signed integers, use SignExtSInt<Type> or ZeroExtSInt<Type> instead.
// static_assert(std::is_same_v<std::int8_t, BitFieldOutputType<std::int8_t, 8>>);
// static_assert(std::is_same_v<std::int16_t, BitFieldOutputType<std::int16_t, 16>>);
// static_assert(std::is_same_v<std::int32_t, BitFieldOutputType<std::int32_t, 32>>);
// static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<std::int64_t, 64>>);
// static_assert(std::is_same_v<S8Enum, BitFieldOutputType<S8Enum, 8>>);
// static_assert(std::is_same_v<S16Enum, BitFieldOutputType<S16Enum, 16>>);
// static_assert(std::is_same_v<S32Enum, BitFieldOutputType<S32Enum, 32>>);
// static_assert(std::is_same_v<S64Enum, BitFieldOutputType<S64Enum, 64>>);
// SignExtSInt<Type>: Type must be a signed integral.
// static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<SignExtSInt<std::uint8_t>, 8>>);
// static_assert(std::is_same_v<float, BitFieldOutputType<SignExtSInt<float>, 32>>);
// static_assert(std::is_same_v<U8Enum, BitFieldOutputType<SignExtSInt<U8Enum>, 8>>);
// static_assert(std::is_same_v<NByteStruct<1>, BitFieldOutputType<SignExtSInt<NByteStruct<1>>, 8>>);
// ZeroExtSInt<Type>: Type must be a signed integral.
// static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<ZeroExtSInt<std::uint8_t>, 8>>);
// static_assert(std::is_same_v<float, BitFieldOutputType<ZeroExtSInt<float>, 32>>);
// static_assert(std::is_same_v<U8Enum, BitFieldOutputType<ZeroExtSInt<U8Enum>, 8>>);
// static_assert(std::is_same_v<NByteStruct<1>, BitFieldOutputType<ZeroExtSInt<NByteStruct<1>>, 8>>);
// Type must be a POD type.
// static_assert(std::is_same_v<NonTrivialStruct, BitFieldOutputType<NonTrivialStruct, 8>>);
// static_assert(std::is_same_v<NonTriviallyCopyableStruct, BitFieldOutputType<NonTriviallyCopyableStruct, 8>>);
// static_assert(std::is_same_v<NonStandardLayoutStruct, BitFieldOutputType<NonStandardLayoutStruct, 16>>);
// static_assert(std::is_same_v<NonPODStruct, BitFieldOutputType<NonPODStruct, 64>>);
// Type must contain at least NumBits bits.
// static_assert(std::is_same_v<std::uint8_t, BitFieldOutputType<std::uint8_t, 9>>);
// static_assert(std::is_same_v<std::uint16_t, BitFieldOutputType<std::uint16_t, 17>>);
// static_assert(std::is_same_v<std::uint32_t, BitFieldOutputType<std::uint32_t, 33>>);
// static_assert(std::is_same_v<std::uint64_t, BitFieldOutputType<std::uint64_t, 65>>);
// static_assert(std::is_same_v<std::uint64_t, BitFieldOutputType<AutoUInt, 65>>);
// static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<AutoSignExtSInt, 65>>);
// static_assert(std::is_same_v<std::int64_t, BitFieldOutputType<AutoZeroExtSInt, 65>>);
// Type must contain exactly NumBits bits.
// static_assert(std::is_same_v<float, BitFieldOutputType<float, 31>>);
// static_assert(std::is_same_v<double, BitFieldOutputType<double, 63>>);
// static_assert(std::is_same_v<NByteStruct<1>, BitFieldOutputType<NByteStruct<1>, 7>>);
// static_assert(std::is_same_v<NByteStruct<2>, BitFieldOutputType<NByteStruct<2>, 15>>);
// static_assert(std::is_same_v<NByteStruct<3>, BitFieldOutputType<NByteStruct<3>, 23>>);
// static_assert(std::is_same_v<NByteStruct<4>, BitFieldOutputType<NByteStruct<4>, 31>>);
// static_assert(std::is_same_v<NByteStruct<5>, BitFieldOutputType<NByteStruct<5>, 39>>);
// static_assert(std::is_same_v<NByteStruct<6>, BitFieldOutputType<NByteStruct<6>, 47>>);
// static_assert(std::is_same_v<NByteStruct<7>, BitFieldOutputType<NByteStruct<7>, 55>>);
// static_assert(std::is_same_v<NByteStruct<8>, BitFieldOutputType<NByteStruct<8>, 63>>);
// clang-format on
} // namespace Common::BitField