forked from 0ad/0ad
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:
parent
7cf2888323
commit
2ef4e7353e
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (header-.less) pool-based heap allocator
|
* (header-less) pool-based heap allocator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
@ -27,10 +27,9 @@
|
|||||||
#include "lib/bits.h"
|
#include "lib/bits.h"
|
||||||
|
|
||||||
|
|
||||||
static const size_t minAlignment = 32;
|
|
||||||
static const bool performSanityChecks = true;
|
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);
|
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
|
// note: we disallow the questionable practice of zero-byte allocations
|
||||||
// because they may be indicative of bugs.
|
// because they may be indicative of bugs.
|
||||||
|
if(size < HeaderlessAllocator::minAllocationSize)
|
||||||
if(size < sizeof(FreedBlock))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(size % minAlignment)
|
if(size % HeaderlessAllocator::allocationGranularity)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -386,8 +384,17 @@ private:
|
|||||||
// user data. this isn't 100% reliable, but as with headers, we don't want
|
// user data. this isn't 100% reliable, but as with headers, we don't want
|
||||||
// to insert extra boundary tags into the allocated memory.
|
// to insert extra boundary tags into the allocated memory.
|
||||||
|
|
||||||
// note: footers are also represented as FreedBlock. this is easier to
|
// note: footers consist of Tag{magic, ID, size}, while headers also
|
||||||
// implement but a bit inefficient since we don't need all its fields.
|
// 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
|
class BoundaryTagManager
|
||||||
{
|
{
|
||||||
@ -633,12 +640,13 @@ public:
|
|||||||
m_stats.OnAllocate(size);
|
m_stats.OnAllocate(size);
|
||||||
|
|
||||||
Validate();
|
Validate();
|
||||||
|
debug_assert((uintptr_t)p % allocationGranularity == 0);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Deallocate(u8* p, size_t size)
|
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(IsValidSize(size));
|
||||||
debug_assert(pool_contains(&m_pool, p));
|
debug_assert(pool_contains(&m_pool, p));
|
||||||
debug_assert(pool_contains(&m_pool, p+size-1));
|
debug_assert(pool_contains(&m_pool, p+size-1));
|
||||||
|
@ -44,6 +44,16 @@
|
|||||||
class HeaderlessAllocator
|
class HeaderlessAllocator
|
||||||
{
|
{
|
||||||
public:
|
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.
|
* @param poolSize maximum amount of memory that can be allocated.
|
||||||
* this much virtual address space is reserved up-front (see Pool).
|
* this much virtual address space is reserved up-front (see Pool).
|
||||||
@ -57,15 +67,16 @@ public:
|
|||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param size [bytes] must be a multiple of the minimum alignment and
|
* @param size [bytes] (= minAllocationSize + i*allocationGranularity).
|
||||||
* enough to store a block header. (this allocator is designed for
|
* (this allocator is designed for requests on the order of several KiB)
|
||||||
* page-aligned requests but can handle smaller amounts.)
|
|
||||||
* @return allocated memory or 0 if the pool is too fragmented or full.
|
* @return allocated memory or 0 if the pool is too fragmented or full.
|
||||||
**/
|
**/
|
||||||
void* Allocate(size_t size) throw();
|
void* Allocate(size_t size) throw();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* deallocate memory.
|
* 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.
|
* @param size must be exactly as specified to Allocate.
|
||||||
**/
|
**/
|
||||||
void Deallocate(void* p, size_t size);
|
void Deallocate(void* p, size_t size);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "lib/self_test.h"
|
#include "lib/self_test.h"
|
||||||
|
|
||||||
|
#include "lib/bits.h" // round_down
|
||||||
#include "lib/allocators/headerless.h"
|
#include "lib/allocators/headerless.h"
|
||||||
|
|
||||||
void* const null = 0;
|
void* const null = 0;
|
||||||
@ -31,10 +32,10 @@ public:
|
|||||||
// (these are disabled because they raise an assert)
|
// (these are disabled because they raise an assert)
|
||||||
#if 0
|
#if 0
|
||||||
// can't Allocate unaligned sizes
|
// 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
|
// can't Allocate too small amounts
|
||||||
TS_ASSERT_EQUALS(a.Allocate(16), null);
|
TS_ASSERT_EQUALS(a.Allocate(HeaderlessAllocator::minAllocationSize/2), null);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// can Allocate the entire pool
|
// can Allocate the entire pool
|
||||||
@ -66,7 +67,7 @@ public:
|
|||||||
HeaderlessAllocator a(0x10000);
|
HeaderlessAllocator a(0x10000);
|
||||||
|
|
||||||
// can Allocate non-power-of-two sizes (note: the current
|
// 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* p1 = a.Allocate(0x5680);
|
||||||
void* p2 = a.Allocate(0x78A0);
|
void* p2 = a.Allocate(0x78A0);
|
||||||
void* p3 = a.Allocate(0x1240);
|
void* p3 = a.Allocate(0x1240);
|
||||||
@ -98,7 +99,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// will the allocator survive a series of random but valid Allocate/Deallocate?
|
// 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;
|
const size_t poolSize = 1024*1024;
|
||||||
HeaderlessAllocator a(poolSize);
|
HeaderlessAllocator a(poolSize);
|
||||||
@ -114,7 +115,7 @@ public:
|
|||||||
if(rand() >= RAND_MAX/2)
|
if(rand() >= RAND_MAX/2)
|
||||||
{
|
{
|
||||||
const size_t maxSize = (size_t)((rand() / (float)RAND_MAX) * poolSize);
|
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);
|
void* p = a.Allocate(size);
|
||||||
if(!p)
|
if(!p)
|
||||||
continue;
|
continue;
|
||||||
|
@ -91,6 +91,8 @@ class TestWdbgSym : public CxxTest::TestSuite
|
|||||||
int ints[] = { 1,2,3,4,5 }; UNUSED2(ints);
|
int ints[] = { 1,2,3,4,5 }; UNUSED2(ints);
|
||||||
wchar_t chars[] = { 'w','c','h','a','r','s',0 }; UNUSED2(chars);
|
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
|
// note: we don't want any kind of dialog to be raised, because
|
||||||
// this test now always runs. therefore, just make sure a decent
|
// this test now always runs. therefore, just make sure a decent
|
||||||
// amount of text (not just "(failed)" error messages) was produced.
|
// amount of text (not just "(failed)" error messages) was produced.
|
||||||
@ -102,6 +104,8 @@ class TestWdbgSym : public CxxTest::TestSuite
|
|||||||
s << text;
|
s << text;
|
||||||
}
|
}
|
||||||
debug_FreeErrorMessage(&emm);
|
debug_FreeErrorMessage(&emm);
|
||||||
|
|
||||||
|
debug_printf(L"(done dumping stack frames)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// also used by test_stl as an element type
|
// 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)
|
// 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)
|
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;
|
size_t l_uint = 0x1234;
|
||||||
bool l_bool = true; UNUSED2(l_bool);
|
bool l_bool = true; UNUSED2(l_bool);
|
||||||
wchar_t l_wchars[] = L"wchar string";
|
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 void* s_ptr = (void*)(uintptr_t)0x87654321;
|
||||||
static HDC s_hdc = (HDC)0xff0;
|
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_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_double addr=%p val=%g\n", &p_double, p_double);
|
||||||
debug_printf(L"p_pchar addr=%p val=%hs\n", &p_pchar, p_pchar);
|
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_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_u8s addr=%p val=%d\n", &l_u8s, l_u8s);
|
||||||
debug_printf(L"l_funcptr addr=%p val=%p\n", &l_funcptr, l_funcptr);
|
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();
|
m_test_stl();
|
||||||
|
|
||||||
|
@ -163,6 +163,8 @@ private:
|
|||||||
return INFO::OK;
|
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
|
// note: this is atomic even on 32-bit CPUs (Pentium MMX and
|
||||||
// above have a 64-bit data bus and MOVQ instruction)
|
// above have a 64-bit data bus and MOVQ instruction)
|
||||||
u64 Read64(size_t offset) const
|
u64 Read64(size_t offset) const
|
||||||
@ -171,7 +173,7 @@ private:
|
|||||||
debug_assert(offset % 8 == 0);
|
debug_assert(offset % 8 == 0);
|
||||||
const uintptr_t address = uintptr_t(m_hpetRegisters)+offset;
|
const uintptr_t address = uintptr_t(m_hpetRegisters)+offset;
|
||||||
const __m128i value128 = _mm_loadl_epi64((__m128i*)address);
|
const __m128i value128 = _mm_loadl_epi64((__m128i*)address);
|
||||||
#if ARCH_AMD64
|
#if HAVE_X64_MOVD
|
||||||
return _mm_cvtsi128_si64x(value128);
|
return _mm_cvtsi128_si64x(value128);
|
||||||
#else
|
#else
|
||||||
__declspec(align(16)) u32 values[4];
|
__declspec(align(16)) u32 values[4];
|
||||||
@ -186,7 +188,7 @@ private:
|
|||||||
debug_assert(offset % 8 == 0);
|
debug_assert(offset % 8 == 0);
|
||||||
debug_assert(offset != CAPS_AND_ID); // can't write to read-only registers
|
debug_assert(offset != CAPS_AND_ID); // can't write to read-only registers
|
||||||
const uintptr_t address = uintptr_t(m_hpetRegisters)+offset;
|
const uintptr_t address = uintptr_t(m_hpetRegisters)+offset;
|
||||||
#if ARCH_AMD64
|
#if HAVE_X64_MOVD
|
||||||
const __m128i value128 = _mm_cvtsi64x_si128(value);
|
const __m128i value128 = _mm_cvtsi64x_si128(value);
|
||||||
#else
|
#else
|
||||||
const __m128i value128 = _mm_set_epi32(0, 0, int(value >> 32), int(value & 0xFFFFFFFF));
|
const __m128i value128 = _mm_set_epi32(0, 0, int(value >> 32), int(value & 0xFFFFFFFF));
|
||||||
|
@ -212,6 +212,28 @@ template<> bool ToPrimitive<size_t>( JSContext* cx, jsval v, size_t& Storage )
|
|||||||
return true;
|
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 ARCH_AMD64
|
||||||
#endif // #if !GCC_VERSION
|
#endif // #if !GCC_VERSION
|
||||||
|
|
||||||
|
@ -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>( const size_t& Native );
|
||||||
template<> jsval ToJSVal<size_t>( 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
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user