Made Remote deallocation work on 32bit

The design of Remote used the top bits of the allocator id to encode
the sizeclass of the deallocation.  On 32bit, or on a platform that uses all the bits
we cannot use these bits for a sizeclass.

This commit uses the bottom bit of the allocator id (which is
guaranteed to be 0), to indicate if the object is the minimum
allocation size.  If it is not the minimum allocation size the
subsequent byte is used to encode the sizeclass.

The code uses constexpr to decide which strategy to use.
This commit is contained in:
Matthew Parkinson 2019-01-16 14:33:58 +00:00
Родитель 272bebb927
Коммит 82595dc9cd
2 изменённых файлов: 54 добавлений и 25 удалений

Просмотреть файл

@ -521,9 +521,7 @@ namespace snmalloc
this->size += sizeclass_to_size(sizeclass);
Remote* r = (Remote*)p;
r->set_sizeclass(sizeclass);
assert(r->sizeclass() == sizeclass);
r->set_target_id(target_id);
r->set_sizeclass_and_target_id(target_id, sizeclass);
assert(r->sizeclass() == sizeclass);
assert(r->target_id() == target_id);
@ -588,10 +586,6 @@ namespace snmalloc
}
};
static_assert(
sizeof(Remote) <= MIN_ALLOC_SIZE,
"Need to be able to cast any small alloc to Remote");
SlabList small_classes[NUM_SMALL_CLASSES];
DLList<Mediumslab> medium_classes[NUM_MEDIUM_CLASSES];

Просмотреть файл

@ -9,11 +9,14 @@ namespace snmalloc
{
struct Remote
{
static const uint64_t SIZECLASS_SHIFT = 56;
static const uint64_t SIZECLASS_MASK = 0xffULL << SIZECLASS_SHIFT;
static const uint64_t TARGET_MASK = ~SIZECLASS_MASK;
static_assert(SIZECLASS_MASK == 0xff00'0000'0000'0000ULL);
static const size_t PTR_BITS = sizeof(void*) * 8;
static const size_t SIZECLASS_BITS = sizeof(uint8_t) * 8;
static const bool USE_TOP_BITS =
SIZECLASS_BITS + bits::ADDRESS_BITS <= PTR_BITS;
static const uintptr_t SIZECLASS_SHIFT = PTR_BITS - SIZECLASS_BITS;
static const uintptr_t SIZECLASS_MASK = ((1ULL << SIZECLASS_BITS) - 1)
<< SIZECLASS_SHIFT;
static const uintptr_t TARGET_MASK = ~SIZECLASS_MASK;
using alloc_id_t = size_t;
union
@ -22,32 +25,64 @@ namespace snmalloc
Remote* non_atomic_next;
};
uint64_t value;
uintptr_t value;
// This will not exist for the minimum object size. This is only used if
// USE_TOP_BITS is false, and the bottom bit of value is set.
uint8_t possible_sizeclass;
void set_target_id(alloc_id_t id)
void set_sizeclass_and_target_id(alloc_id_t id, uint8_t sizeclass)
{
assert(id == (id & TARGET_MASK));
value = (id & TARGET_MASK) | (value & SIZECLASS_MASK);
}
void set_sizeclass(uint8_t sizeclass)
{
value = (value & TARGET_MASK) |
((static_cast<uint64_t>(sizeclass) << SIZECLASS_SHIFT) &
SIZECLASS_MASK);
if constexpr (USE_TOP_BITS)
{
assert(id == (id & TARGET_MASK));
value = (id & TARGET_MASK) |
((static_cast<uint64_t>(sizeclass) << SIZECLASS_SHIFT) &
SIZECLASS_MASK);
}
else
{
assert((id & 1) == 0);
if (sizeclass == 0)
{
value = id | 1;
}
else
{
value = id;
possible_sizeclass = sizeclass;
}
}
}
alloc_id_t target_id()
{
return value & TARGET_MASK;
if constexpr (USE_TOP_BITS)
{
return value & TARGET_MASK;
}
else
{
return value & ~1;
}
}
uint8_t sizeclass()
{
return (value & SIZECLASS_MASK) >> SIZECLASS_SHIFT;
if constexpr (USE_TOP_BITS)
{
return (value & SIZECLASS_MASK) >> SIZECLASS_SHIFT;
}
else
{
return ((value & 1) == 1) ? 0 : possible_sizeclass;
}
}
};
static_assert(
(offsetof(Remote, possible_sizeclass)) <= MIN_ALLOC_SIZE,
"Need to be able to cast any small alloc to Remote");
struct RemoteAllocator
{
using alloc_id_t = Remote::alloc_id_t;