mirror of
https://github.com/corda/corda.git
synced 2025-01-07 13:38:47 +00:00
301 lines
5.5 KiB
C++
301 lines
5.5 KiB
C++
#include "heapwalk.h"
|
|
|
|
using namespace vm;
|
|
|
|
namespace {
|
|
|
|
const uintptr_t PointerShift = log(BytesPerWord);
|
|
|
|
class Context;
|
|
|
|
class Set: public HeapMap {
|
|
public:
|
|
class Entry {
|
|
public:
|
|
object value;
|
|
uint32_t number;
|
|
int next;
|
|
};
|
|
|
|
static unsigned footprint(unsigned capacity) {
|
|
return sizeof(Set)
|
|
+ pad(sizeof(int) * capacity)
|
|
+ pad(sizeof(Set::Entry) * capacity);
|
|
}
|
|
|
|
Set(Context* context, unsigned capacity):
|
|
context(context),
|
|
index(reinterpret_cast<int*>
|
|
(reinterpret_cast<uint8_t*>(this)
|
|
+ sizeof(Set))),
|
|
entries(reinterpret_cast<Entry*>
|
|
(reinterpret_cast<uint8_t*>(index)
|
|
+ pad(sizeof(int) * capacity))),
|
|
size(0),
|
|
capacity(capacity)
|
|
{ }
|
|
|
|
virtual int find(object value);
|
|
|
|
virtual void dispose();
|
|
|
|
Context* context;
|
|
int* index;
|
|
Entry* entries;
|
|
unsigned size;
|
|
unsigned capacity;
|
|
};
|
|
|
|
class Stack {
|
|
public:
|
|
class Entry {
|
|
public:
|
|
object value;
|
|
int offset;
|
|
};
|
|
|
|
static const unsigned Capacity = 4096;
|
|
|
|
Stack(Stack* next): next(next), entryCount(0) { }
|
|
|
|
Stack* next;
|
|
unsigned entryCount;
|
|
Entry entries[Capacity];
|
|
};
|
|
|
|
class Context {
|
|
public:
|
|
Context(Thread* thread):
|
|
thread(thread), objects(0), stack(0)
|
|
{ }
|
|
|
|
~Context() {
|
|
while (stack) {
|
|
Stack* dead = stack;
|
|
stack = dead->next;
|
|
thread->m->heap->free(stack, sizeof(Stack));
|
|
}
|
|
}
|
|
|
|
Thread* thread;
|
|
Set* objects;
|
|
Stack* stack;
|
|
};
|
|
|
|
void
|
|
push(Context* c, object p, int offset)
|
|
{
|
|
if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) {
|
|
c->stack = new (c->thread->m->heap->allocate(sizeof(Stack)))
|
|
Stack(c->stack);
|
|
}
|
|
Stack::Entry* e = c->stack->entries + (c->stack->entryCount++);
|
|
e->value = p;
|
|
e->offset = offset;
|
|
}
|
|
|
|
bool
|
|
pop(Context* c, object* p, int* offset)
|
|
{
|
|
if (c->stack) {
|
|
if (c->stack->entryCount == 0) {
|
|
if (c->stack->next) {
|
|
Stack* dead = c->stack;
|
|
c->stack = dead->next;
|
|
c->thread->m->heap->free(dead, sizeof(Stack));
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
Stack::Entry* e = c->stack->entries + (--c->stack->entryCount);
|
|
*p = e->value;
|
|
*offset = e->offset;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
unsigned
|
|
hash(object p, unsigned capacity)
|
|
{
|
|
return (reinterpret_cast<uintptr_t>(p) >> PointerShift)
|
|
& (capacity - 1);
|
|
}
|
|
|
|
Set::Entry*
|
|
find(Context* c, object p)
|
|
{
|
|
if (c->objects == 0) return 0;
|
|
|
|
for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) {
|
|
Set::Entry* e = c->objects->entries + i;
|
|
if (e->value == p) {
|
|
return e;
|
|
}
|
|
i = e->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Set::find(object value)
|
|
{
|
|
Set::Entry* e = ::find(context, value);
|
|
if (e) {
|
|
return e->number;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
Set::dispose()
|
|
{
|
|
context->thread->m->heap->free(this, footprint(capacity));
|
|
}
|
|
|
|
Set::Entry*
|
|
add(Context* c UNUSED, Set* set, object p, uint32_t number)
|
|
{
|
|
assert(c->thread, set->size < set->capacity);
|
|
|
|
unsigned index = hash(p, set->capacity);
|
|
|
|
int offset = set->size++;
|
|
Set::Entry* e = set->entries + offset;
|
|
e->value = p;
|
|
e->number = number;
|
|
e->next = set->index[index];
|
|
set->index[index] = offset;
|
|
return e;
|
|
}
|
|
|
|
Set::Entry*
|
|
add(Context* c, object p)
|
|
{
|
|
if (c->objects == 0 or c->objects->size == c->objects->capacity) {
|
|
unsigned capacity;
|
|
if (c->objects) {
|
|
capacity = c->objects->capacity * 2;
|
|
} else {
|
|
capacity = 4096; // must be power of two
|
|
}
|
|
|
|
Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity)))
|
|
Set(c, capacity);
|
|
|
|
memset(set->index, 0xFF, sizeof(int) * capacity);
|
|
|
|
if (c->objects) {
|
|
for (unsigned i = 0; i < c->objects->capacity; ++i) {
|
|
for (int j = c->objects->index[i]; j >= 0;) {
|
|
Set::Entry* e = c->objects->entries + j;
|
|
add(c, set, e->value, e->number);
|
|
j = e->next;
|
|
}
|
|
}
|
|
|
|
c->thread->m->heap->free
|
|
(c->objects, Set::footprint(c->objects->capacity));
|
|
}
|
|
|
|
c->objects = set;
|
|
}
|
|
|
|
return add(c, c->objects, p, 0);
|
|
}
|
|
|
|
inline object
|
|
get(object o, unsigned offsetInWords)
|
|
{
|
|
return static_cast<object>
|
|
(mask(cast<void*>(o, offsetInWords * BytesPerWord)));
|
|
}
|
|
|
|
unsigned
|
|
objectSize(Thread* t, object o)
|
|
{
|
|
unsigned n = baseSize(t, o, objectClass(t, o));
|
|
if (objectExtended(t, o)) {
|
|
++ n;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void
|
|
walk(Context* c, HeapWalker* w, object p)
|
|
{
|
|
Thread* t = c->thread;
|
|
int nextChildOffset;
|
|
|
|
w->root();
|
|
|
|
visit: {
|
|
Set::Entry* e = find(c, p);
|
|
if (e) {
|
|
w->visitOld(p, e->number);
|
|
} else {
|
|
e = add(c, p);
|
|
e->number = w->visitNew(p);
|
|
|
|
nextChildOffset = walkNext(t, p, -1);
|
|
if (nextChildOffset != -1) {
|
|
goto children;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto pop;
|
|
|
|
children: {
|
|
w->push(nextChildOffset);
|
|
push(c, p, nextChildOffset);
|
|
p = get(p, nextChildOffset);
|
|
goto visit;
|
|
}
|
|
|
|
pop: {
|
|
if (pop(c, &p, &nextChildOffset)) {
|
|
w->pop();
|
|
nextChildOffset = walkNext(t, p, nextChildOffset);
|
|
if (nextChildOffset >= 0) {
|
|
goto children;
|
|
} else {
|
|
goto pop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace vm {
|
|
|
|
HeapMap*
|
|
walk(Thread* t, HeapWalker* w)
|
|
{
|
|
Context context(t);
|
|
|
|
class Visitor: public Heap::Visitor {
|
|
public:
|
|
Visitor(Context* c, HeapWalker* w): c(c), w(w) { }
|
|
|
|
virtual void visit(void* p) {
|
|
walk(c, w, static_cast<object>(mask(*static_cast<void**>(p))));
|
|
}
|
|
|
|
Context* c;
|
|
HeapWalker* w;
|
|
} v(&context, w);
|
|
|
|
add(&context, 0)->number = w->visitNew(0);
|
|
|
|
visitRoots(t->m, &v);
|
|
|
|
return context.objects;
|
|
}
|
|
|
|
} // namespace vm
|