This commit is contained in:
Victor Olin 2023-02-18 15:05:02 +01:00
parent c6c0468c8d
commit c20ef8f5dd
4 changed files with 135 additions and 110 deletions

47
src/GC/docs/heap.md Normal file
View file

@ -0,0 +1,47 @@
## Heap Documentation
### Algorithm notes
void mark_test(vector<Chunk *> worklist) {
while (worklist.size() > 0) {
Chunk *ref = worklist.pop_back();
Chunk *child = (Chunk*) *ref;
if (child != NULL && !child->marked) {
child->marked = true;
worklist.push_back(child);
mark_test(worklist);
}
}
}
void mark_from_roots(uintptr_t *start, const uintptr_t *end) {
vector<Chunk *> worklist;
for (;start > end; start--) {
Chunk *ref = *start;
if (ref != NULL && !ref->marked) {
ref->marked = true;
worklist.push_back(ref);
mark_test(worklist);
}
}
}
Alternative marking, pseudocode
mark_from_roots():
worklist <- empty
for fld in Roots
ref <- *fld
if ref ≠ null && !marked(ref)
set_marked(ref)
worklist.add(ref)
mark()
mark():
while size(worklist) > 0
ref <- remove_first(worklist)
for fld in Pointers(ref)
child <- *fld
if child ≠ null && !marked(child)
set_marked(child)
worklist.add(child)

View file

@ -31,9 +31,9 @@ namespace GC {
void collect(); void collect();
void sweep(); void sweep();
uintptr_t *try_recycle_chunks(size_t size);
void free(); void free();
void free_overlap(); void free_overlap();
// void compact();
void mark(uintptr_t *start, const uintptr_t *end, std::vector<Chunk *> worklist); void mark(uintptr_t *start, const uintptr_t *end, std::vector<Chunk *> worklist);
void print_line(Chunk *chunk); void print_line(Chunk *chunk);
void print_worklist(std::vector<Chunk *> list); void print_worklist(std::vector<Chunk *> list);
@ -42,21 +42,14 @@ namespace GC {
const char *m_heap; const char *m_heap;
size_t m_size; size_t m_size;
size_t m_allocated_size; size_t m_allocated_size;
uintptr_t *m_stack_end = nullptr;
std::vector<Chunk *> m_allocated_chunks; std::vector<Chunk *> m_allocated_chunks;
std::vector<Chunk *> m_freed_chunks; std::vector<Chunk *> m_freed_chunks;
public: public:
// Singleton static Heap *the() {
static Heap &the() {
if (m_instance)
return *m_instance;
m_instance = new Heap();
return *m_instance;
}
static Heap *the2() {
if (m_instance) if (m_instance)
return m_instance; return m_instance;
m_instance = new Heap(); m_instance = new Heap();
@ -64,10 +57,11 @@ namespace GC {
} }
// BEWARE only for testing, this should be adressed // BEWARE only for testing, this should be adressed
/* ~Heap() { ~Heap() {
free((char *)m_heap); std::free((char *)m_heap);
} */ }
void init();
void *alloc(size_t size); void *alloc(size_t size);
void print_contents(); void print_contents();
void collect(uint flags); // DEBUG ONLY void collect(uint flags); // DEBUG ONLY

View file

@ -12,45 +12,50 @@ using namespace std;
namespace GC { namespace GC {
// alloc assumes that after the collect phase, the aligned memory in the heap is compacted from the start, /**
* Initialises the heap singleton and saves the address
* of the calling stack frame as the stack_end. Presumeably
* this address points to the stack frame of the compiled
* LLVM executable after linking. (NOT CONFIRMED)
*/
void Heap::init() {
Heap *heap = Heap::the();
heap->m_stack_end = reinterpret_cast<uintptr_t *>(__builtin_frame_address(1));
}
/**
* Allocates a given amount of bytes on the heap.
*
* @param size The amount of bytes to be allocated.
*
* @return A pointer to the address where the memory
* has been allocated. This pointer is supposed
* to be casted to and object pointer.
*/
void *Heap::alloc(size_t size) { void *Heap::alloc(size_t size) {
auto heap = Heap::the();
assert(size > 0 && "Heap: Cannot alloc less than 0B"); // Singleton
if (heap.m_size + size > HEAP_SIZE) { Heap *heap = Heap::the();
// try collect
if (size < 0) {
cout << "Heap: Cannot alloc less than 0B. No bytes allocated." << endl;
return nullptr;
}
if (heap->m_size + size > HEAP_SIZE) {
collect(); collect();
assert(heap.m_size + size <= HEAP_SIZE && "Heap: Out Of Memory"); // If collect failed, crash with OOM error
assert(heap->m_size + size <= HEAP_SIZE && "Heap: Out Of Memory");
} }
// kolla freed chunks innan // If a chunk was recycled, return the old chunk address
// denna är helt onödig just nu, freed chunks kommer alltid va tom uintptr_t *reused_chunk = try_recycle_chunks(size);
for (size_t i = 0; i < m_freed_chunks.size(); i++) { if (reused_chunk != nullptr) {
auto cp = m_freed_chunks.at(i); return (void *)reused_chunk;
if (cp->size > size)
{
// dela upp chunken och sno ena delen
size_t diff = cp->size - size;
auto chunk_complement = new Chunk;
chunk_complement->size = diff;
chunk_complement->start = cp->start + cp->size;
m_freed_chunks.erase(m_freed_chunks.begin() + i);
m_freed_chunks.push_back(chunk_complement);
m_allocated_chunks.push_back(cp);
return cp->start;
}
else if (cp->size == size)
{
// sno hela chunken
m_freed_chunks.erase(m_freed_chunks.begin() + i);
m_allocated_chunks.push_back(cp);
return cp->start;
}
} }
// Om inga free chunks finns, skapa ny chunk // If no free chunks was found (reused_chunk is a nullptr),
// then create a new chunk
auto new_chunk = new Chunk; auto new_chunk = new Chunk;
new_chunk->size = size; new_chunk->size = size;
new_chunk->start = (uintptr_t *)m_heap + m_size; new_chunk->start = (uintptr_t *)m_heap + m_size;
@ -63,32 +68,55 @@ namespace GC {
return new_chunk->start; return new_chunk->start;
} }
uintptr_t *Heap::try_recycle_chunks(size_t size) {
auto heap = Heap::the();
// Check if there are any freed chunks large enough for current request
for (size_t i = 0; i < heap->m_freed_chunks.size(); i++) {
auto cp = heap->m_freed_chunks.at(i);
if (cp->size > size)
{
// Split the chunk, use one part and add the remaining part to
// the list of freed chunks
size_t diff = cp->size - size;
auto chunk_complement = new Chunk;
chunk_complement->size = diff;
chunk_complement->start = cp->start + cp->size;
heap->m_freed_chunks.erase(m_freed_chunks.begin() + i);
heap->m_freed_chunks.push_back(chunk_complement);
heap->m_allocated_chunks.push_back(cp);
return cp->start;
}
else if (cp->size == size)
{
// Reuse the whole chunk
m_freed_chunks.erase(m_freed_chunks.begin() + i);
m_allocated_chunks.push_back(cp);
return cp->start;
}
}
}
void Heap::collect() { void Heap::collect() {
// Get the adress of the current stack frame
auto heap = Heap::the();
uintptr_t *stack_end;
// get the frame adress, whwere local variables and saved registers are located
auto stack_start = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0)); auto stack_start = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
// looking at 10 stack frames back if (heap->m_stack_end != nullptr)
const uintptr_t *stack_end = (uintptr_t *)0; // temporary stack_end = heap->m_stack_end;
else
stack_end = (uintptr_t *)0; // temporary
// denna segfaultar om arg för __b_f_a är > 2
// reinterpret_cast<const uintptr_t *>(__builtin_frame_address(10));
auto work_list = m_allocated_chunks; auto work_list = m_allocated_chunks;
mark(stack_start, stack_end, work_list); mark(stack_start, stack_end, work_list);
sweep(); sweep();
// compact();
free(); free();
// We shouldn't do this, since then m_freed_chunks doesnt' have any real purpose,
// it should be used in alloc, it isn't if we delete *all* of its contentes
// release free chunks
// while (m_freed_chunks.size()) {
// auto chunk_pointer = m_freed_chunks.back();
// m_freed_chunks.pop_back();
// delete chunk_pointer; // deletes chunk object, doesn't free memory to the OS
// }
} }
void Heap::free() { void Heap::free() {
@ -200,50 +228,6 @@ namespace GC {
} }
} }
/* void mark_test(vector<Chunk *> worklist) {
while (worklist.size() > 0) {
Chunk *ref = worklist.pop_back();
Chunk *child = (Chunk*) *ref;
if (child != NULL && !child->marked) {
child->marked = true;
worklist.push_back(child);
mark_test(worklist);
}
}
}
void mark_from_roots(uintptr_t *start, const uintptr_t *end) {
vector<Chunk *> worklist;
for (;start > end; start--) {
Chunk *ref = *start;
if (ref != NULL && !ref->marked) {
ref->marked = true;
worklist.push_back(ref);
mark_test(worklist);
}
}
} */
/* Alternative marking, pseudocode
mark_from_roots():
worklist <- empty
for fld in Roots
ref <- *fld
if ref null && !marked(ref)
set_marked(ref)
worklist.add(ref)
mark()
mark():
while size(worklist) > 0
ref <- remove_first(worklist)
for fld in Pointers(ref)
child <- *fld
if child null && !marked(child)
set_marked(child)
worklist.add(child)
*/
// For testing purposes // For testing purposes
void Heap::print_line(Chunk *chunk) { void Heap::print_line(Chunk *chunk) {
cout << "Marked: " << chunk->marked << "\nStart adr: " << chunk->start << "\nSize: " << chunk->size << " B\n" << endl; cout << "Marked: " << chunk->marked << "\nStart adr: " << chunk->start << "\nSize: " << chunk->size << " B\n" << endl;

View file

@ -7,7 +7,7 @@ Goal for next week (24/2):
## GC TODO: ## GC TODO:
- Merge to main branch - Merge to main branch
- Don't empty m_free_chunks, reuse in a better way **Victor fixes this** - Fix singleton references
## Tests TODO ## Tests TODO
### Library linking ### Library linking