This commit is contained in:
Morph 2024-03-03 02:04:55 +13:00 committed by GitHub
commit 82bb95cf98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 620 additions and 0 deletions

View File

@ -94,6 +94,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

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

@ -0,0 +1,402 @@
// 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;
// C++20: Signed Integers are Twos Complement
// Left-shift on signed integer types produces the same results as
// left-shift on the corresponding unsigned integer type.
// Right-shift is an arithmetic right shift which performs sign-extension.
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