Files
swift-mirror/unittests/Basic/PointerIntEnumTest.cpp
Michael Gottesman 9fa6e31f9e [projection] Add a new data structure for use in NewProjection called PointerIntEnum.
PointerIntEnum is a more powerful PointerIntPair data structure. It uses
an enum with special cases to understand characteristics of the data and
then uses this information and the some tricks to be able to
represent:

1. Up to tagged bit number of pointer cases. The cases are stored inline.
2. Inline indices up to 4096.
3. Out of line indices > 4096.

It takes advantage of the trick that we use in the runtime already to
distinguish pointers from indices: namely that the zero page on modern
OSes do not allocate the zero page.

I made unittests for all of the operations so it is pretty well tested
out.

I am going to use this in a subsequent commit to compress projection in
the common case (the inline case) down to 1/3 of its size. The reason
why the inline case is common is that in most cases where projection is
used it will be targeting relative offsets in an array which are not
likely to be greater than a page. The mallocing of memory just enables
us to degrade gracefully.
2015-12-29 22:06:13 -06:00

509 lines
14 KiB
C++

#include "swift/Basic/PointerIntEnum.h"
#include "llvm/ADT/ArrayRef.h"
#include "gtest/gtest.h"
using namespace swift;
namespace {
enum class EnumTy : uint64_t {
Invalid = 0,
// Pointer Kinds
Ptr1 = 1,
Ptr2 = 2,
Ptr3 = 3,
LastPointerKind = Ptr3,
// Index Kinds.
//
// When we have an index >= 4096, we malloc memory to store it. It needs to be
// able to be stored in at most 3 bits.
LargeIndex = 7,
Index1 = 8,
Index2 = 9,
Index3 = 10,
Index4 = 11,
Index5 = 12,
LastIndexKind = Index5,
};
using PointerIntEnumTy =
PointerIntEnum<EnumTy, void *, 3, 4, llvm::PointerLikeTypeTraits<void *>,
true>;
} // end anonymous namespace
TEST(PointerIntEnumTest, DefaultConstructIsInvalid) {
PointerIntEnumTy Enum;
EXPECT_FALSE(Enum.isValid());
EXPECT_TRUE(Enum.getKind() == EnumTy::Invalid);
}
TEST(PointerIntEnumTest, ConstructDestructInt) {
llvm::ArrayRef<EnumTy> Cases((EnumTy[5]){EnumTy::Index1, EnumTy::Index2,
EnumTy::Index3, EnumTy::Index4,
EnumTy::Index5},
5);
for (auto &Case : Cases) {
for (unsigned i = 0, e = 50; i < e; ++i) {
PointerIntEnumTy Enum(Case, i);
EXPECT_TRUE(Enum.isValid());
EXPECT_EQ(Enum.getKind(), Case);
EXPECT_EQ(Enum.getIndex(), i);
EXPECT_EQ(Enum.getRawKind(), Case);
}
for (unsigned i = 0, e = 4096; i < e; ++i) {
PointerIntEnumTy Enum(Case, i);
EXPECT_TRUE(Enum.isValid());
EXPECT_EQ(Enum.getKind(), Case);
EXPECT_EQ(Enum.getIndex(), i);
EXPECT_EQ(Enum.getRawKind(), Case);
}
for (unsigned i = 4096, e = 10000; i < e; ++i) {
void *ptr;
uint64_t x;
{
PointerIntEnumTy Enum(Case, i);
EXPECT_TRUE(Enum.isValid());
EXPECT_EQ(Enum.getKind(), Case);
EXPECT_EQ(Enum.getIndex(), i);
EXPECT_EQ(Enum.getRawKind(), EnumTy::LargeIndex);
ptr = Enum.getPointer();
memcpy(&x, ptr, sizeof(x));
}
uint64_t y;
memcpy(&y, ptr, sizeof(y));
EXPECT_NE(y, x);
EXPECT_EQ(y, -1ULL);
}
}
}
TEST(PointerIntEnumTest, ConstructDestructPointer) {
EnumTy *Enums = new EnumTy[3];
Enums[0] = EnumTy::Ptr1;
Enums[1] = EnumTy::Ptr2;
Enums[2] = EnumTy::Ptr3;
for (unsigned ii = 0, ie = 3; ii < ie; ++ii) {
for (unsigned jj = 0, je = 3; jj < je; ++jj) {
void *Ptr = reinterpret_cast<void *>(&Enums[jj]);
PointerIntEnumTy Enum(Enums[ii], Ptr);
EXPECT_TRUE(Enum.isValid());
EXPECT_EQ(Enum.getKind(), Enums[ii]);
EXPECT_EQ(Enum.getPointer(), Ptr);
EXPECT_EQ(Enum.getRawKind(), Enums[ii]);
}
}
delete[] Enums;
}
TEST(PointerIntEnumTest, CopyConstructorLargeInt) {
PointerIntEnumTy E(EnumTy::Index2, 5000);
PointerIntEnumTy E2(E);
EXPECT_TRUE(E.isValid());
EXPECT_TRUE(E2.isValid());
EXPECT_EQ(E.getKind(), E2.getKind());
EXPECT_NE(E.getPointer(), E2.getPointer());
EXPECT_EQ(E.getRawKind(), E2.getRawKind());
EXPECT_EQ(E.getIndex(), E2.getIndex());
}
TEST(PointerIntEnumTest, CopyConstructorSmallInt) {
PointerIntEnumTy E(EnumTy::Index3, 5);
PointerIntEnumTy E2(E);
EXPECT_TRUE(E.isValid());
EXPECT_TRUE(E2.isValid());
EXPECT_EQ(E.getKind(), E2.getKind());
EXPECT_EQ(E.getRawKind(), E2.getRawKind());
EXPECT_EQ(E.getIndex(), E2.getIndex());
}
TEST(PointerIntEnumTest, CopyConstructorPointer) {
int *ptr = new int[1];
PointerIntEnumTy E(EnumTy::Ptr1, reinterpret_cast<void *>(ptr));
PointerIntEnumTy E2(E);
EXPECT_TRUE(E.isValid());
EXPECT_TRUE(E2.isValid());
EXPECT_EQ(E.getKind(), E2.getKind());
EXPECT_EQ(E.getPointer(), E2.getPointer());
EXPECT_EQ(E.getRawKind(), E2.getRawKind());
delete [] ptr;
}
TEST(PointerIntEnumTest, MoveConstructorLargeInt) {
PointerIntEnumTy E(EnumTy::Index2, 5000);
void *Ptr = E.getPointer();
{
PointerIntEnumTy E2(std::move(E));
EXPECT_FALSE(E.isValid());
EXPECT_EQ(E.getKind(), EnumTy::Invalid);
EXPECT_EQ(E.getRawKind(), EnumTy::Invalid);
EXPECT_TRUE(E2.isValid());
EXPECT_EQ(E2.getKind(), EnumTy::Index2);
EXPECT_EQ(E2.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(E2.getIndex(), 5000U);
EXPECT_EQ(E2.getPointer(), Ptr);
}
uint64_t y;
memcpy(&y, Ptr, sizeof(y));
EXPECT_EQ(y, -1ULL);
}
TEST(PointerIntEnumTest, MoveConstructorSmallInt) {
PointerIntEnumTy E(EnumTy::Index2, 4095);
PointerIntEnumTy E2(std::move(E));
EXPECT_FALSE(E.isValid());
EXPECT_EQ(E.getKind(), EnumTy::Invalid);
EXPECT_EQ(E.getRawKind(), EnumTy::Invalid);
EXPECT_TRUE(E2.isValid());
EXPECT_EQ(E2.getKind(), EnumTy::Index2);
EXPECT_EQ(E2.getRawKind(), EnumTy::Index2);
EXPECT_EQ(E2.getIndex(), 4095U);
}
TEST(PointerIntEnumTest, MoveConstructorPointer) {
int *InputPtr = new int[1];
InputPtr[0] = INT_MAX;
PointerIntEnumTy E(EnumTy::Ptr3, InputPtr);
void *Ptr = E.getPointer();
EXPECT_EQ(Ptr, InputPtr);
{
PointerIntEnumTy E2(std::move(E));
EXPECT_FALSE(E.isValid());
EXPECT_EQ(E.getKind(), EnumTy::Invalid);
EXPECT_EQ(E.getRawKind(), EnumTy::Invalid);
EXPECT_TRUE(E2.isValid());
EXPECT_EQ(E2.getKind(), EnumTy::Ptr3);
EXPECT_EQ(E2.getRawKind(), EnumTy::Ptr3);
EXPECT_EQ(E2.getPointer(), Ptr);
}
EXPECT_EQ(InputPtr[0], INT_MAX);
delete [] InputPtr;
}
TEST(PointerIntEnumTest, CopyAssignInvalidToLargeInt) {
PointerIntEnumTy Invalid;
PointerIntEnumTy Large(EnumTy::Index3, 5000);
Invalid = Large;
EXPECT_TRUE(Invalid.isValid());
EXPECT_EQ(Invalid.getKind(), EnumTy::Index3);
EXPECT_EQ(Invalid.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Invalid.getIndex(), 5000U);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Index3);
EXPECT_EQ(Large.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Large.getIndex(), 5000U);
}
TEST(PointerIntEnumTest, CopyAssignSmallIntToLargeInt) {
PointerIntEnumTy Small(EnumTy::Index2, 4095);
PointerIntEnumTy Large(EnumTy::Index3, 5000);
Small = Large;
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Index3);
EXPECT_EQ(Small.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Small.getIndex(), 5000U);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Index3);
EXPECT_EQ(Large.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Large.getIndex(), 5000U);
}
TEST(PointerIntEnumTest, CopyAssignLargeIntToSmallInt) {
PointerIntEnumTy Small(EnumTy::Index2, 4095);
PointerIntEnumTy Large(EnumTy::Index3, 5000);
void *Ptr = Large.getPointer();
Large = Small;
uint64_t y;
memcpy(&y, Ptr, sizeof(y));
EXPECT_EQ(y, -1ULL);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Index2);
EXPECT_EQ(Small.getRawKind(), EnumTy::Index2);
EXPECT_EQ(Small.getIndex(), 4095U);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Index2);
EXPECT_EQ(Large.getRawKind(), EnumTy::Index2);
EXPECT_EQ(Large.getIndex(), 4095U);
}
TEST(PointerIntEnumTest, CopyAssignPointerToLargeInt) {
int *InputPtr = new int[1];
InputPtr[0] = INT_MAX;
void *Ptr1, *Ptr2;
uint64_t Ptr1Value, Ptr2Value;
{
PointerIntEnumTy Large(EnumTy::Index3, 5000);
Ptr1 = Large.getPointer();
memcpy(&Ptr1Value, Ptr1, sizeof(Ptr1Value));
EXPECT_NE(Ptr1Value, -1ULL);
{
PointerIntEnumTy Pointer(EnumTy::Ptr3, InputPtr);
void *Ptr = Pointer.getPointer();
EXPECT_EQ(Ptr, InputPtr);
Pointer = Large;
Ptr2 = Pointer.getPointer();
EXPECT_NE(Ptr1, Ptr2);
memcpy(&Ptr2Value, Ptr2, sizeof(Ptr2Value));
EXPECT_NE(Ptr2Value, -1ULL);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Index3);
EXPECT_EQ(Large.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Large.getIndex(), 5000U);
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Index3);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Pointer.getIndex(), 5000U);
}
memcpy(&Ptr2Value, Ptr2, sizeof(Ptr2Value));
EXPECT_EQ(Ptr2Value, -1ULL);
memcpy(&Ptr1Value, Ptr1, sizeof(Ptr1Value));
EXPECT_NE(Ptr1Value, -1ULL);
}
memcpy(&Ptr1Value, Ptr1, sizeof(Ptr1Value));
EXPECT_EQ(Ptr1Value, -1ULL);
delete [] InputPtr;
}
TEST(PointerIntEnumTest, CopyAssignLargeIntToPointer) {
int *InputPtr = new int[1];
InputPtr[0] = INT_MAX;
PointerIntEnumTy Pointer(EnumTy::Ptr3, InputPtr);
PointerIntEnumTy Large(EnumTy::Index3, 5000);
void *Ptr = Large.getPointer();
uint64_t Value;
memcpy(&Value, Ptr, sizeof(Value));
EXPECT_NE(Value, -1ULL);
Large = Pointer;
memcpy(&Value, Ptr, sizeof(Value));
EXPECT_EQ(Value, -1ULL);
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Ptr3);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::Ptr3);
EXPECT_EQ(Pointer.getPointer(), InputPtr);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Ptr3);
EXPECT_EQ(Large.getRawKind(), EnumTy::Ptr3);
EXPECT_EQ(Large.getPointer(), InputPtr);
delete [] InputPtr;
}
TEST(PointerIntEnumTest, CopyAssignSmallIntToPointer) {
int *InputPtr = new int[1];
InputPtr[0] = INT_MAX;
PointerIntEnumTy Pointer(EnumTy::Ptr3, InputPtr);
PointerIntEnumTy Small(EnumTy::Index3, 4095);
Small = Pointer;
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Ptr3);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::Ptr3);
EXPECT_EQ(Pointer.getPointer(), InputPtr);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Ptr3);
EXPECT_EQ(Small.getRawKind(), EnumTy::Ptr3);
EXPECT_EQ(Small.getPointer(), InputPtr);
delete [] InputPtr;
}
TEST(PointerIntEnumTest, CopyAssignPointerToSmallInt) {
int *InputPtr = new int[1];
InputPtr[0] = INT_MAX;
PointerIntEnumTy Pointer(EnumTy::Ptr3, InputPtr);
PointerIntEnumTy Small(EnumTy::Index3, 4095);
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Ptr3);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::Ptr3);
EXPECT_EQ(Pointer.getPointer(), InputPtr);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Index3);
EXPECT_EQ(Small.getRawKind(), EnumTy::Index3);
EXPECT_EQ(Small.getIndex(), 4095U);
Pointer = Small;
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Index3);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::Index3);
EXPECT_EQ(Pointer.getIndex(), 4095U);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Index3);
EXPECT_EQ(Small.getRawKind(), EnumTy::Index3);
EXPECT_EQ(Small.getIndex(), 4095U);
delete [] InputPtr;
}
TEST(PointerIntEnumTest, MoveAssignSmallIntToLargeInt) {
PointerIntEnumTy Small(EnumTy::Index2, 4095);
PointerIntEnumTy Large(EnumTy::Index3, 5000);
Small = std::move(Large);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Index3);
EXPECT_EQ(Small.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Small.getIndex(), 5000U);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Index2);
EXPECT_EQ(Large.getRawKind(), EnumTy::Index2);
EXPECT_EQ(Large.getIndex(), 4095U);
}
TEST(PointerIntEnumTest, MoveAssignLargeIntToSmallInt) {
PointerIntEnumTy Small(EnumTy::Index2, 4095);
PointerIntEnumTy Large(EnumTy::Index3, 5000);
Large = std::move(Small);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Index3);
EXPECT_EQ(Small.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Small.getIndex(), 5000U);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Index2);
EXPECT_EQ(Large.getRawKind(), EnumTy::Index2);
EXPECT_EQ(Large.getIndex(), 4095U);
}
TEST(PointerIntEnumTest, MoveAssignPointerToLargeInt) {
int *IntPtr = new int[1];
PointerIntEnumTy Pointer(EnumTy::Ptr1, IntPtr);
PointerIntEnumTy Large(EnumTy::Index3, 5000);
Pointer = std::move(Large);
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Index3);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Pointer.getIndex(), 5000U);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Ptr1);
EXPECT_EQ(Large.getRawKind(), EnumTy::Ptr1);
EXPECT_EQ(Large.getPointer(), IntPtr);
delete [] IntPtr;
}
TEST(PointerIntEnumTest, MoveAssignLargeIntToPointer) {
int *IntPtr = new int[1];
PointerIntEnumTy Pointer(EnumTy::Ptr1, IntPtr);
PointerIntEnumTy Large(EnumTy::Index3, 5000);
Large = std::move(Pointer);
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Index3);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::LargeIndex);
EXPECT_EQ(Pointer.getIndex(), 5000U);
EXPECT_TRUE(Large.isValid());
EXPECT_EQ(Large.getKind(), EnumTy::Ptr1);
EXPECT_EQ(Large.getRawKind(), EnumTy::Ptr1);
EXPECT_EQ(Large.getPointer(), IntPtr);
delete [] IntPtr;
}
TEST(PointerIntEnumTest, MoveAssignSmallIntToPointer) {
int *IntPtr = new int[1];
PointerIntEnumTy Pointer(EnumTy::Ptr1, IntPtr);
PointerIntEnumTy Small(EnumTy::Index2, 4095U);
Pointer = std::move(Small);
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Index2);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::Index2);
EXPECT_EQ(Pointer.getIndex(), 4095U);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Ptr1);
EXPECT_EQ(Small.getRawKind(), EnumTy::Ptr1);
EXPECT_EQ(Small.getPointer(), IntPtr);
delete [] IntPtr;
}
TEST(PointerIntEnumTest, MoveAssignPointerToSmallInt) {
int *IntPtr = new int[1];
PointerIntEnumTy Pointer(EnumTy::Ptr1, IntPtr);
PointerIntEnumTy Small(EnumTy::Index2, 4095U);
Small = std::move(Pointer);
EXPECT_TRUE(Pointer.isValid());
EXPECT_EQ(Pointer.getKind(), EnumTy::Index2);
EXPECT_EQ(Pointer.getRawKind(), EnumTy::Index2);
EXPECT_EQ(Pointer.getIndex(), 4095U);
EXPECT_TRUE(Small.isValid());
EXPECT_EQ(Small.getKind(), EnumTy::Ptr1);
EXPECT_EQ(Small.getRawKind(), EnumTy::Ptr1);
EXPECT_EQ(Small.getPointer(), IntPtr);
delete [] IntPtr;
}