From 2ef4e7353ec6bf23450fb0a89ee98f82c27ea3ee Mon Sep 17 00:00:00 2001 From: janwas Date: Sat, 7 Nov 2009 12:26:40 +0000 Subject: [PATCH] self-test and x64 fixes: - headerless: distinguish between allocation granularity and min size (hopefully fixes test failure on x64) - test_wdbg_sym: disable printf spew - hpet: use workaround when x64 MOVD isn't available due to processor or compiler - JSConversions: VC2005 x64 apparently distinguishes ssize_t from long int (as it does with size_t) This was SVN commit r7179. --- source/lib/allocators/headerless.cpp | 26 ++++++++++++------- source/lib/allocators/headerless.h | 17 +++++++++--- source/lib/allocators/tests/test_headerless.h | 11 ++++---- .../lib/sysdep/os/win/tests/test_wdbg_sym.h | 19 ++++++++++++-- source/lib/sysdep/os/win/whrt/hpet.cpp | 6 +++-- source/scripting/JSConversions.cpp | 22 ++++++++++++++++ source/scripting/JSConversions.h | 5 ++++ 7 files changed, 85 insertions(+), 21 deletions(-) diff --git a/source/lib/allocators/headerless.cpp b/source/lib/allocators/headerless.cpp index 46b8954a8b..55ec9b2bbf 100644 --- a/source/lib/allocators/headerless.cpp +++ b/source/lib/allocators/headerless.cpp @@ -16,7 +16,7 @@ */ /* - * (header-.less) pool-based heap allocator + * (header-less) pool-based heap allocator */ #include "precompiled.h" @@ -27,10 +27,9 @@ #include "lib/bits.h" -static const size_t minAlignment = 32; static const bool performSanityChecks = true; -// shared by the Impl::Allocate and FreedBlock::Validate +// shared by Impl::Allocate and FreedBlock::Validate static bool IsValidSize(size_t size); @@ -121,11 +120,10 @@ static bool IsValidSize(size_t size) { // note: we disallow the questionable practice of zero-byte allocations // because they may be indicative of bugs. - - if(size < sizeof(FreedBlock)) + if(size < HeaderlessAllocator::minAllocationSize) return false; - if(size % minAlignment) + if(size % HeaderlessAllocator::allocationGranularity) return false; return true; @@ -386,8 +384,17 @@ private: // user data. this isn't 100% reliable, but as with headers, we don't want // to insert extra boundary tags into the allocated memory. -// note: footers are also represented as FreedBlock. this is easier to -// implement but a bit inefficient since we don't need all its fields. +// note: footers consist of Tag{magic, ID, size}, while headers also +// need prev/next pointers. this could comfortably fit in 64 bytes, +// but we don't want to inherit headers from a base class because its +// prev/next pointers should reside between the magic and ID fields. +// maintaining separate FreedBlock and Footer classes is also undesirable; +// we prefer to use FreedBlock for both, which increases the minimum +// allocation size to 64 + allocationGranularity, e.g. 128. +// that's not a problem because the allocator is designed for +// returning pages or IO buffers (4..256 KB). +cassert(HeaderlessAllocator::minAllocationSize >= 2*sizeof(FreedBlock)); + class BoundaryTagManager { @@ -633,12 +640,13 @@ public: m_stats.OnAllocate(size); Validate(); + debug_assert((uintptr_t)p % allocationGranularity == 0); return p; } void Deallocate(u8* p, size_t size) { - debug_assert((uintptr_t)p % minAlignment == 0); + debug_assert((uintptr_t)p % allocationGranularity == 0); debug_assert(IsValidSize(size)); debug_assert(pool_contains(&m_pool, p)); debug_assert(pool_contains(&m_pool, p+size-1)); diff --git a/source/lib/allocators/headerless.h b/source/lib/allocators/headerless.h index 279dce773d..6e57a2b5fa 100644 --- a/source/lib/allocators/headerless.h +++ b/source/lib/allocators/headerless.h @@ -44,6 +44,16 @@ class HeaderlessAllocator { public: + // allocators must 'naturally' align pointers, i.e. ensure they are + // multiples of the largest native type (currently __m128). + // since there are no headers, we can guarantee alignment by + // requiring sizes to be multiples of allocationGranularity. + static const size_t allocationGranularity = 16; + + // allocations must be large enough to hold our boundary tags + // when freed. (see rationale above BoundaryTagManager) + static const size_t minAllocationSize = 128; + /** * @param poolSize maximum amount of memory that can be allocated. * this much virtual address space is reserved up-front (see Pool). @@ -57,15 +67,16 @@ public: void Reset(); /** - * @param size [bytes] must be a multiple of the minimum alignment and - * enough to store a block header. (this allocator is designed for - * page-aligned requests but can handle smaller amounts.) + * @param size [bytes] (= minAllocationSize + i*allocationGranularity). + * (this allocator is designed for requests on the order of several KiB) * @return allocated memory or 0 if the pool is too fragmented or full. **/ void* Allocate(size_t size) throw(); /** * deallocate memory. + * @param p must be exactly as returned by Allocate (in particular, + * evenly divisible by allocationGranularity) * @param size must be exactly as specified to Allocate. **/ void Deallocate(void* p, size_t size); diff --git a/source/lib/allocators/tests/test_headerless.h b/source/lib/allocators/tests/test_headerless.h index 5c0d37a402..dd86995334 100644 --- a/source/lib/allocators/tests/test_headerless.h +++ b/source/lib/allocators/tests/test_headerless.h @@ -17,6 +17,7 @@ #include "lib/self_test.h" +#include "lib/bits.h" // round_down #include "lib/allocators/headerless.h" void* const null = 0; @@ -31,10 +32,10 @@ public: // (these are disabled because they raise an assert) #if 0 // can't Allocate unaligned sizes - TS_ASSERT_EQUALS(a.Allocate(1), null); + TS_ASSERT_EQUALS(a.Allocate(HeaderlessAllocator::minAllocationSize+1), null); // can't Allocate too small amounts - TS_ASSERT_EQUALS(a.Allocate(16), null); + TS_ASSERT_EQUALS(a.Allocate(HeaderlessAllocator::minAllocationSize/2), null); #endif // can Allocate the entire pool @@ -66,7 +67,7 @@ public: HeaderlessAllocator a(0x10000); // can Allocate non-power-of-two sizes (note: the current - // implementation only allows sizes that are multiples of 0x20) + // implementation only allows sizes that are multiples of 0x10) void* p1 = a.Allocate(0x5680); void* p2 = a.Allocate(0x78A0); void* p3 = a.Allocate(0x1240); @@ -98,7 +99,7 @@ public: } // will the allocator survive a series of random but valid Allocate/Deallocate? - void DISABLED_test_Randomized() // XXX: No it won't (on Linux/amd64) + void test_Randomized() // XXX: No it won't (on Linux/amd64) { const size_t poolSize = 1024*1024; HeaderlessAllocator a(poolSize); @@ -114,7 +115,7 @@ public: if(rand() >= RAND_MAX/2) { const size_t maxSize = (size_t)((rand() / (float)RAND_MAX) * poolSize); - const size_t size = maxSize & ~0x1Fu; + const size_t size = std::max(HeaderlessAllocator::minAllocationSize, round_down(maxSize, HeaderlessAllocator::allocationGranularity)); void* p = a.Allocate(size); if(!p) continue; diff --git a/source/lib/sysdep/os/win/tests/test_wdbg_sym.h b/source/lib/sysdep/os/win/tests/test_wdbg_sym.h index 87bec435e1..7c673f387c 100644 --- a/source/lib/sysdep/os/win/tests/test_wdbg_sym.h +++ b/source/lib/sysdep/os/win/tests/test_wdbg_sym.h @@ -91,6 +91,8 @@ class TestWdbgSym : public CxxTest::TestSuite int ints[] = { 1,2,3,4,5 }; UNUSED2(ints); wchar_t chars[] = { 'w','c','h','a','r','s',0 }; UNUSED2(chars); + debug_printf(L"\n(dumping stack frames may result in access violations..)\n"); + // note: we don't want any kind of dialog to be raised, because // this test now always runs. therefore, just make sure a decent // amount of text (not just "(failed)" error messages) was produced. @@ -102,6 +104,8 @@ class TestWdbgSym : public CxxTest::TestSuite s << text; } debug_FreeErrorMessage(&emm); + + debug_printf(L"(done dumping stack frames)\n"); } // also used by test_stl as an element type @@ -253,8 +257,6 @@ class TestWdbgSym : public CxxTest::TestSuite // anyway (to see at a glance whether symbol engine addrs are correct) static void m_test_addrs(int p_int, double p_double, char* p_pchar, uintptr_t p_uintptr) { - debug_printf(L"\nTEST_ADDRS\n"); - size_t l_uint = 0x1234; bool l_bool = true; UNUSED2(l_bool); wchar_t l_wchars[] = L"wchar string"; @@ -268,6 +270,8 @@ class TestWdbgSym : public CxxTest::TestSuite static void* s_ptr = (void*)(uintptr_t)0x87654321; static HDC s_hdc = (HDC)0xff0; +#if 0 // output only needed when debugging + debug_printf(L"\nTEST_ADDRS\n"); debug_printf(L"p_int addr=%p val=%d\n", &p_int, p_int); debug_printf(L"p_double addr=%p val=%g\n", &p_double, p_double); debug_printf(L"p_pchar addr=%p val=%hs\n", &p_pchar, p_pchar); @@ -278,6 +282,17 @@ class TestWdbgSym : public CxxTest::TestSuite debug_printf(L"l_enum addr=%p val=%d\n", &l_enum, l_enum); debug_printf(L"l_u8s addr=%p val=%d\n", &l_u8s, l_u8s); debug_printf(L"l_funcptr addr=%p val=%p\n", &l_funcptr, l_funcptr); +#else + UNUSED2(p_uintptr); + UNUSED2(p_pchar); + UNUSED2(p_double); + UNUSED2(p_int); + UNUSED2(l_funcptr); + UNUSED2(l_enum); + UNUSED2(l_u8s); + UNUSED2(l_uint); + UNUSED2(l_wchars); +#endif m_test_stl(); diff --git a/source/lib/sysdep/os/win/whrt/hpet.cpp b/source/lib/sysdep/os/win/whrt/hpet.cpp index 91c8c5371a..a7a2b20cec 100644 --- a/source/lib/sysdep/os/win/whrt/hpet.cpp +++ b/source/lib/sysdep/os/win/whrt/hpet.cpp @@ -163,6 +163,8 @@ private: return INFO::OK; } +#define HAVE_X64_MOVD ARCH_AMD64 && (ICC_VERSION || MSC_VERSION >= 1500) + // note: this is atomic even on 32-bit CPUs (Pentium MMX and // above have a 64-bit data bus and MOVQ instruction) u64 Read64(size_t offset) const @@ -171,7 +173,7 @@ private: debug_assert(offset % 8 == 0); const uintptr_t address = uintptr_t(m_hpetRegisters)+offset; const __m128i value128 = _mm_loadl_epi64((__m128i*)address); -#if ARCH_AMD64 +#if HAVE_X64_MOVD return _mm_cvtsi128_si64x(value128); #else __declspec(align(16)) u32 values[4]; @@ -186,7 +188,7 @@ private: debug_assert(offset % 8 == 0); debug_assert(offset != CAPS_AND_ID); // can't write to read-only registers const uintptr_t address = uintptr_t(m_hpetRegisters)+offset; -#if ARCH_AMD64 +#if HAVE_X64_MOVD const __m128i value128 = _mm_cvtsi64x_si128(value); #else const __m128i value128 = _mm_set_epi32(0, 0, int(value >> 32), int(value & 0xFFFFFFFF)); diff --git a/source/scripting/JSConversions.cpp b/source/scripting/JSConversions.cpp index 22dda2ee33..484cb1b0c6 100644 --- a/source/scripting/JSConversions.cpp +++ b/source/scripting/JSConversions.cpp @@ -212,6 +212,28 @@ template<> bool ToPrimitive( JSContext* cx, jsval v, size_t& Storage ) return true; } + +template<> jsval ToJSVal( const ssize_t& Native ) +{ + return( INT_TO_JSVAL( (int)Native ) ); +} + +template<> jsval ToJSVal( ssize_t& Native ) +{ + return( INT_TO_JSVAL( (int)Native ) ); +} + +template<> bool ToPrimitive( JSContext* cx, jsval v, ssize_t& Storage ) +{ + int temp; + if(!ToPrimitive(cx, v, temp)) + return false; + if(temp < 0) + return false; + Storage = (ssize_t)temp; + return true; +} + #endif // #if ARCH_AMD64 #endif // #if !GCC_VERSION diff --git a/source/scripting/JSConversions.h b/source/scripting/JSConversions.h index 35b9c1ac6c..798472559e 100644 --- a/source/scripting/JSConversions.h +++ b/source/scripting/JSConversions.h @@ -165,6 +165,11 @@ template<> bool ToPrimitive( JSContext* cx, jsval v, size_t& Storage ); template<> jsval ToJSVal( const size_t& Native ); template<> jsval ToJSVal( size_t& Native ); +// ssize_t +template<> bool ToPrimitive( JSContext* cx, jsval v, ssize_t& Storage ); +template<> jsval ToJSVal( const ssize_t& Native ); +template<> jsval ToJSVal( ssize_t& Native ); + #endif #endif