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.
This commit is contained in:
janwas 2009-11-07 12:26:40 +00:00
parent 7cf2888323
commit 2ef4e7353e
7 changed files with 85 additions and 21 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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));

View File

@ -212,6 +212,28 @@ template<> bool ToPrimitive<size_t>( JSContext* cx, jsval v, size_t& Storage )
return true;
}
template<> jsval ToJSVal<ssize_t>( const ssize_t& Native )
{
return( INT_TO_JSVAL( (int)Native ) );
}
template<> jsval ToJSVal<ssize_t>( ssize_t& Native )
{
return( INT_TO_JSVAL( (int)Native ) );
}
template<> bool ToPrimitive<ssize_t>( JSContext* cx, jsval v, ssize_t& Storage )
{
int temp;
if(!ToPrimitive<int>(cx, v, temp))
return false;
if(temp < 0)
return false;
Storage = (ssize_t)temp;
return true;
}
#endif // #if ARCH_AMD64
#endif // #if !GCC_VERSION

View File

@ -165,6 +165,11 @@ template<> bool ToPrimitive<size_t>( JSContext* cx, jsval v, size_t& Storage );
template<> jsval ToJSVal<size_t>( const size_t& Native );
template<> jsval ToJSVal<size_t>( size_t& Native );
// ssize_t
template<> bool ToPrimitive<ssize_t>( JSContext* cx, jsval v, ssize_t& Storage );
template<> jsval ToJSVal<ssize_t>( const ssize_t& Native );
template<> jsval ToJSVal<ssize_t>( ssize_t& Native );
#endif
#endif