commit
fc71494742
|
@ -36,6 +36,8 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Abstract bitfield class
|
* Abstract bitfield class
|
||||||
*
|
*
|
||||||
|
@ -92,13 +94,23 @@
|
||||||
*
|
*
|
||||||
* Caveats:
|
* Caveats:
|
||||||
*
|
*
|
||||||
|
* 1)
|
||||||
* BitField provides automatic casting from and to the storage type where
|
* BitField provides automatic casting from and to the storage type where
|
||||||
* appropriate. However, when using non-typesafe functions like printf, an
|
* appropriate. However, when using non-typesafe functions like printf, an
|
||||||
* explicit cast must be performed on the BitField object to make sure it gets
|
* explicit cast must be performed on the BitField object to make sure it gets
|
||||||
* passed correctly, e.g.:
|
* passed correctly, e.g.:
|
||||||
* printf("Value: %d", (s32)some_register.some_signed_fields);
|
* printf("Value: %d", (s32)some_register.some_signed_fields);
|
||||||
*
|
*
|
||||||
|
* 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<std::size_t position, std::size_t bits, typename T>
|
template<std::size_t position, std::size_t bits, typename T>
|
||||||
struct BitField
|
struct BitField
|
||||||
{
|
{
|
||||||
|
@ -113,13 +125,13 @@ public:
|
||||||
// so that we can use this within unions
|
// so that we can use this within unions
|
||||||
BitField() = default;
|
BitField() = default;
|
||||||
|
|
||||||
BitField& operator=(T val)
|
__forceinline BitField& operator=(T val)
|
||||||
{
|
{
|
||||||
storage = (storage & ~GetMask()) | ((val << position) & GetMask());
|
storage = (storage & ~GetMask()) | ((val << position) & GetMask());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator T() const
|
__forceinline operator T() const
|
||||||
{
|
{
|
||||||
if (std::numeric_limits<T>::is_signed)
|
if (std::numeric_limits<T>::is_signed)
|
||||||
{
|
{
|
||||||
|
@ -144,7 +156,7 @@ private:
|
||||||
// Unsigned version of StorageType
|
// Unsigned version of StorageType
|
||||||
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
||||||
|
|
||||||
StorageType GetMask() const
|
__forceinline StorageType GetMask() const
|
||||||
{
|
{
|
||||||
return ((~(StorageTypeU)0) >> (8*sizeof(T) - bits)) << position;
|
return ((~(StorageTypeU)0) >> (8*sizeof(T) - bits)) << position;
|
||||||
}
|
}
|
||||||
|
@ -158,3 +170,4 @@ private:
|
||||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||||
static_assert(bits > 0, "Invalid number of bits");
|
static_assert(bits > 0, "Invalid number of bits");
|
||||||
};
|
};
|
||||||
|
#pragma pack()
|
||||||
|
|
|
@ -125,3 +125,45 @@ TEST(BitField, Assignment)
|
||||||
EXPECT_EQ(object.regular_field_unsigned, object.at_dword_boundary);
|
EXPECT_EQ(object.regular_field_unsigned, object.at_dword_boundary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test class behavior on oddly aligned structures.
|
||||||
|
TEST(BitField, Alignment)
|
||||||
|
{
|
||||||
|
#pragma pack(1)
|
||||||
|
struct OddlyAlignedTestStruct
|
||||||
|
{
|
||||||
|
u8 padding;
|
||||||
|
TestUnion obj;
|
||||||
|
};
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
GC_ALIGNED16(OddlyAlignedTestStruct test_struct);
|
||||||
|
TestUnion& object = test_struct.obj;
|
||||||
|
static_assert(alignof(test_struct.obj.signed_1bit) == 1, "Incorrect variable alignment");
|
||||||
|
|
||||||
|
for (u64 val : table)
|
||||||
|
{
|
||||||
|
// Assignments with fixed values
|
||||||
|
object.full_u64 = val;
|
||||||
|
EXPECT_EQ(val, object.full_u64);
|
||||||
|
|
||||||
|
object.full_s64 = (s64)val;
|
||||||
|
EXPECT_EQ((s64)val, object.full_u64);
|
||||||
|
|
||||||
|
object.regular_field_unsigned = val;
|
||||||
|
EXPECT_EQ(val & 0x7, object.regular_field_unsigned);
|
||||||
|
|
||||||
|
object.at_dword_boundary = val;
|
||||||
|
EXPECT_EQ(((s64)(val << 60)) >> 60, object.at_dword_boundary);
|
||||||
|
|
||||||
|
object.signed_1bit = val;
|
||||||
|
EXPECT_EQ((val & 1) ? -1 : 0, object.signed_1bit);
|
||||||
|
|
||||||
|
object.regular_field_signed = val;
|
||||||
|
EXPECT_EQ(((s64)(object.hex << 61)) >> 61, object.regular_field_signed);
|
||||||
|
|
||||||
|
// Assignment from other BitField
|
||||||
|
object.at_dword_boundary = object.regular_field_unsigned;
|
||||||
|
EXPECT_EQ(object.regular_field_unsigned, object.at_dword_boundary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue