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"
|
||||
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user