/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// #include #include #include // Conditionally include SSE headers which some of the tests conditionally rely upon. #if EA_SSE #if EA_SSE >= 1 #include #endif #if EA_SSE >= 2 #include #endif #if EA_SSE >= 1 EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(__m128) EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(__m128i) #endif #endif using namespace EA::StdC; using namespace EA::UnitTest; /////////////////////////////////////////////////////////////////////////////// // TestStruct // // Helper class for our unit test. // struct TestStruct { uint32_t a; uint64_t b; char c; void* d; TestStruct() : a(0), b(0), c(0), d(0) {} }; /////////////////////////////////////////////////////////////////////////////// // TestTemplate // // Helper class for our unit test. // template struct TestTemplate { uint32_t a; uint64_t b; char c; void* d; TestTemplate() : a(0), b(0), c(0), d(0) {} }; /////////////////////////////////////////////////////////////////////////////// // TestAlignment // int TestAlignment() { int nErrorCount(0); /////////////////////////////////////////////////////////////////////////// // EAAlignOf /////////////////////////////////////////////////////////////////////////// { size_t a; // Type-based AlignOf a = EAAlignOf(uint32_t); EATEST_VERIFY(a == sizeof(uint32_t)); #if (EA_PLATFORM_WORD_SIZE >= 8) a = EAAlignOf(uint64_t); EATEST_VERIFY(a == sizeof(uint64_t)); #endif a = EAAlignOf(TestStruct); EATEST_VERIFY(((a * 2) / 2) == a); // This should always be true. a = EAAlignOf(TestTemplate); EATEST_VERIFY(((a * 2) / 2) == a); // This should always be true. // Instance-based AlignOf uint32_t x32 = 0; a = AlignOf(x32); EATEST_VERIFY(a == sizeof(uint32_t)); #if (EA_PLATFORM_WORD_SIZE >= 8) uint64_t x64 = 0; a = AlignOf(x64); EATEST_VERIFY(a == sizeof(uint64_t)); #endif TestStruct testStruct; a = AlignOf(testStruct); EATEST_VERIFY(((a * 2) / 2) == a); // This should always be true. TestTemplate testTemplate; a = AlignOf(testTemplate); EATEST_VERIFY(((a * 2) / 2) == a); // This should always be true. } /////////////////////////////////////////////////////////////////////////// // AlignAddressUp, AlignObjectUp, AlignAddressDown, AlignObjectDown, GetAlignment /////////////////////////////////////////////////////////////////////////// { void* pResult; for(char* p = 0; p < (char*)100000; p++) { for(size_t i = 0; i < 18; i++) { uintptr_t a = ((size_t)1 << i); pResult = AlignAddressUp(p, a); EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0); pResult = AlignObjectUp(p, a); EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0); pResult = AlignAddressDown(p, ((size_t)1 << i)); EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0); pResult = AlignObjectDown(p, ((size_t)1 << i)); EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0); size_t alignment = GetAlignment(pResult); EATEST_VERIFY(alignment >= a); // We test >= because sometimes values are coincidentally aligned to a higher value than intended. Which is OK. bool bResult = IsAligned(pResult, a); EATEST_VERIFY(bResult); } } for(TestStruct* p = 0, *pTestStruct; p < (TestStruct*)100000; p++) { for(size_t i = 0; i < 18; i++) { uintptr_t a = ((size_t)1 << i); pTestStruct = AlignObjectUp(p, a); EATEST_VERIFY(((uintptr_t)pTestStruct % a) == (uintptr_t)0); pTestStruct = AlignObjectDown(p, ((size_t)1 << i)); EATEST_VERIFY(((uintptr_t)pTestStruct % a) == (uintptr_t)0); size_t alignment = GetAlignment(pTestStruct); EATEST_VERIFY(alignment >= a); // We test >= because sometimes values are coincidentally aligned to a higher value than intended. Which is OK. bool bResult = IsAligned(pTestStruct, a); EATEST_VERIFY(bResult); } } } /////////////////////////////////////////////////////////////////////////// // AlignAddressUp /////////////////////////////////////////////////////////////////////////// { for(uintptr_t address = 0; address < 1024; ++address) { for(size_t a = 1; a < 1024*1024; a <<= 1) { const void* up = AlignAddressUp ((void*)address, a); const void* down = AlignAddressDown((void*)address, a); EATEST_VERIFY(((uintptr_t)up >= address) && IsAligned(up, a)); EATEST_VERIFY(((uintptr_t)down <= address) && IsAligned(down, a)); } } } /////////////////////////////////////////////////////////////////////////// // AlignUp, AlignDown /////////////////////////////////////////////////////////////////////////// { for(uint8_t u8 = 0; u8 < 150; ++u8) { for(size_t a = 1; a < 64; a <<= 1) { uint8_t n = AlignUp(u8, a); EATEST_VERIFY(IsAligned(n, a) && (n >= u8)); n = AlignDown(u8, a); EATEST_VERIFY(IsAligned(n, a) && (n <= u8)); } } for(int16_t i16 = 0; i16 < 10000; ++i16) { for(size_t a = 1; a < 1024; a <<= 1) { int16_t n = AlignUp(i16, a); EATEST_VERIFY(IsAligned(n, a) && (n >= i16)); n = AlignDown(i16, a); EATEST_VERIFY(IsAligned(n, a) && (n <= i16)); } } for(uint32_t u32 = 0; u32 < 10000; ++u32) { for(size_t a = 1; a < 1024; a <<= 1) { uint32_t n = AlignUp(u32, a); EATEST_VERIFY(IsAligned(n, a) && (n >= u32)); n = AlignDown(u32, a); EATEST_VERIFY(IsAligned(n, a) && (n <= u32)); } } for(int64_t i64 = 0; i64 < 10000; ++i64) { for(size_t a = 1; a < 1024; a <<= 1) { int64_t n = AlignUp(i64, a); EATEST_VERIFY(IsAligned(n, a) && (n >= i64)); n = AlignDown(i64, a); EATEST_VERIFY(IsAligned(n, a) && (n <= i64)); } } for(char* p8 = 0; p8 < (char*)(uintptr_t)1000; ++p8) { for(size_t a = 1; a < 64; a <<= 1) { TestStruct* pTestStruct = (TestStruct*)(uintptr_t)p8; pTestStruct = AlignUp(pTestStruct, a); EATEST_VERIFY(IsAligned(pTestStruct, a) && ((uintptr_t)pTestStruct >= (uintptr_t)p8)); pTestStruct = (TestStruct*)(uintptr_t)p8; pTestStruct = AlignDown(pTestStruct, a); EATEST_VERIFY(IsAligned(pTestStruct, a) && ((uintptr_t)pTestStruct <= (uintptr_t)p8)); } } for(uint64_t x = 0; x < 1024; ++x) { for(size_t a = 1; a < 1024*1024; a <<= 1) { const uint64_t up = AlignUp(x, a); const uint64_t down = AlignDown(x, a); EATEST_VERIFY((up >= x) && IsAligned(up, a)); EATEST_VERIFY((down <= x) && IsAligned(up, a)); const uint64_t up1 = AlignUp (x); const uint64_t down1 = AlignDown(x); EATEST_VERIFY((up1 >= x) && IsAligned(up1, 1)); EATEST_VERIFY((down1 <= x) && IsAligned(up1, 1)); const uint64_t up2 = AlignUp (x); const uint64_t down2 = AlignDown(x); EATEST_VERIFY((up2 >= x) && IsAligned(up2, 2)); EATEST_VERIFY((down2 <= x) && IsAligned(up2, 2)); const uint64_t up4 = AlignUp (x); const uint64_t down4 = AlignDown(x); EATEST_VERIFY((up4 >= x) && IsAligned(up4, 4)); EATEST_VERIFY((down4 <= x) && IsAligned(up4, 4)); const uint64_t up8 = AlignUp (x); const uint64_t down8 = AlignDown(x); EATEST_VERIFY((up8 >= x) && IsAligned(up8, 8)); EATEST_VERIFY((down8 <= x) && IsAligned(up8, 8)); const uint64_t up16 = AlignUp (x); const uint64_t down16 = AlignDown(x); EATEST_VERIFY((up16 >= x) && IsAligned(up16, 16)); EATEST_VERIFY((down16 <= x) && IsAligned(up16, 16)); } } // Try again with address values around INT32_MAX for(uint64_t x = 0x7ffffff0; x < 0x800000ff; ++x) { for(size_t a = 1; a < 1024*1024; a <<= 1) { const uint64_t up = AlignUp(x, a); const uint64_t down = AlignDown(x, a); EATEST_VERIFY((up >= x) && IsAligned(up, a)); EATEST_VERIFY((down <= x) && IsAligned(up, a)); const uint64_t up1 = AlignUp (x); const uint64_t down1 = AlignDown(x); EATEST_VERIFY((up1 >= x) && IsAligned(up1, 1)); EATEST_VERIFY((down1 <= x) && IsAligned(up1, 1)); const uint64_t up2 = AlignUp (x); const uint64_t down2 = AlignDown(x); EATEST_VERIFY((up2 >= x) && IsAligned(up2, 2)); EATEST_VERIFY((down2 <= x) && IsAligned(up2, 2)); const uint64_t up4 = AlignUp (x); const uint64_t down4 = AlignDown(x); EATEST_VERIFY((up4 >= x) && IsAligned(up4, 4)); EATEST_VERIFY((down4 <= x) && IsAligned(up4, 4)); const uint64_t up8 = AlignUp (x); const uint64_t down8 = AlignDown(x); EATEST_VERIFY((up8 >= x) && IsAligned(up8, 8)); EATEST_VERIFY((down8 <= x) && IsAligned(up8, 8)); const uint64_t up16 = AlignUp (x); const uint64_t down16 = AlignDown(x); EATEST_VERIFY((up16 >= x) && IsAligned(up16, 16)); EATEST_VERIFY((down16 <= x) && IsAligned(up16, 16)); } } } /////////////////////////////////////////////////////////////////////////// // IsAligned /////////////////////////////////////////////////////////////////////////// { // template // inline bool IsAligned(T x) bool b = IsAligned(64); EATEST_VERIFY(b); b = IsAligned(67); EATEST_VERIFY(!b); // template // inline bool IsAligned(T* p) b = IsAligned((int*)NULL + 0); EATEST_VERIFY(b); b = IsAligned((int*)NULL + 1); EATEST_VERIFY(!b); // template // inline bool IsAligned(T x, size_t a) EATEST_VERIFY( IsAligned(64, 8)); EATEST_VERIFY(!IsAligned(67, 8)); // template // inline bool IsAligned(T* p, size_t a) EATEST_VERIFY( IsAligned((int*)NULL + 0, 64)); EATEST_VERIFY(!IsAligned((int*)NULL + 1, 64)); } /////////////////////////////////////////////////////////////////////////// // AlignedType /////////////////////////////////////////////////////////////////////////// { // Disabled until we can find a way that works for all compilers. #if 0 // !defined(__GNUC__) || (__GNUC__ < 4) // GCC4 lost the ability to do this const size_t kAlignment = 128; AlignedType::Type intAlignedAt128; EATEST_VERIFY(((uintptr_t)&intAlignedAt128) % kAlignment == (uintptr_t)0); intAlignedAt128 = 12345; EATEST_VERIFY(intAlignedAt128 == 12345); #endif } /////////////////////////////////////////////////////////////////////////// // AlignedArray /////////////////////////////////////////////////////////////////////////// { const size_t kSize = 10; const size_t kAlignment = 64; { AlignedArray mTestStructArray; // Note that as of this writing the convention is that only the first element // of the array should be aligned. Other elements should follow the first as // they naturally would otherwise. EATEST_VERIFY(((uintptr_t)&mTestStructArray[0]) % kAlignment == (uintptr_t)0); mTestStructArray[0] = mTestStructArray[1]; AlignedArray mTestStructArray2(mTestStructArray); mTestStructArray = mTestStructArray2; } // These tests instantiate SSE types to ensure they are supported by the AlignedArray. Previously, // these tests didn't work on Clang. #if defined EA_SSE && (EA_SSE >= 1) { AlignedArray<__m128, kSize, kAlignment> mTestVectorArray; // Note that as of this writing the convention is that only the first element // of the array should be aligned. Other elements should follow the first as // they naturally would otherwise. EATEST_VERIFY(((uintptr_t)&mTestVectorArray[0]) % kAlignment == (uintptr_t)0); mTestVectorArray[0] = mTestVectorArray[1]; } #endif #if defined EA_SSE && (EA_SSE >= 2) { AlignedArray<__m128i, kSize, kAlignment> mTestVectorArray; // Note that as of this writing the convention is that only the first element // of the array should be aligned. Other elements should follow the first as // they naturally would otherwise. EATEST_VERIFY(((uintptr_t)&mTestVectorArray[0]) % kAlignment == (uintptr_t)0); mTestVectorArray[0] = mTestVectorArray[1]; } #endif } /////////////////////////////////////////////////////////////////////////// // AlignedObject /////////////////////////////////////////////////////////////////////////// { const size_t kAlignment = 64; AlignedObject mTestStruct; EATEST_VERIFY((uintptr_t)&mTestStruct % kAlignment == (uintptr_t)0); mTestStruct->a = 1; EATEST_VERIFY(mTestStruct->a == (unsigned)1); // (*(&mTestStruct))->b = 2; This is not possible; we need to use '.' (*(&mTestStruct)).b = 2; EATEST_VERIFY(mTestStruct->b == (unsigned)2); AlignedObject mTestStruct2(mTestStruct); mTestStruct = mTestStruct2; // Todo: Test the following // T* operator &() // T* Get() // const T* Get() const // operator T&() // operator const T&() const // T* operator->() // const T* operator->() const // operator T*() // operator const T*() const } return nErrorCount; }