From 518940ab153caf9df70d5f7b68ac5cf984290f25 Mon Sep 17 00:00:00 2001 From: Victor Olin Date: Sat, 18 Feb 2023 15:05:02 +0100 Subject: [PATCH] clean up --- src/GC/docs/heap.md | 47 +++++++++++ src/GC/include/heap.hpp | 20 ++--- src/GC/lib/heap.cpp | 176 ++++++++++++++++++---------------------- src/GC/todo.md | 2 +- 4 files changed, 135 insertions(+), 110 deletions(-) create mode 100644 src/GC/docs/heap.md diff --git a/src/GC/docs/heap.md b/src/GC/docs/heap.md new file mode 100644 index 0000000..5db98e6 --- /dev/null +++ b/src/GC/docs/heap.md @@ -0,0 +1,47 @@ +## Heap Documentation + +### Algorithm notes + + void mark_test(vector 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 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) \ No newline at end of file diff --git a/src/GC/include/heap.hpp b/src/GC/include/heap.hpp index 0bd3d36..0962c9f 100644 --- a/src/GC/include/heap.hpp +++ b/src/GC/include/heap.hpp @@ -31,9 +31,9 @@ namespace GC { void collect(); void sweep(); + uintptr_t *try_recycle_chunks(size_t size); void free(); void free_overlap(); - // void compact(); void mark(uintptr_t *start, const uintptr_t *end, std::vector worklist); void print_line(Chunk *chunk); void print_worklist(std::vector list); @@ -42,21 +42,14 @@ namespace GC { const char *m_heap; size_t m_size; size_t m_allocated_size; + uintptr_t *m_stack_end = nullptr; std::vector m_allocated_chunks; std::vector m_freed_chunks; public: - // Singleton - static Heap &the() { - if (m_instance) - return *m_instance; - m_instance = new Heap(); - return *m_instance; - } - - static Heap *the2() { + static Heap *the() { if (m_instance) return m_instance; m_instance = new Heap(); @@ -64,10 +57,11 @@ namespace GC { } // BEWARE only for testing, this should be adressed -/* ~Heap() { - free((char *)m_heap); - } */ + ~Heap() { + std::free((char *)m_heap); + } + void init(); void *alloc(size_t size); void print_contents(); void collect(uint flags); // DEBUG ONLY diff --git a/src/GC/lib/heap.cpp b/src/GC/lib/heap.cpp index 480fc3b..d6ac52d 100644 --- a/src/GC/lib/heap.cpp +++ b/src/GC/lib/heap.cpp @@ -12,45 +12,50 @@ using namespace std; namespace GC { - // alloc assumes that after the collect phase, the aligned memory in the heap is compacted from the start, - void *Heap::alloc(size_t size) { - auto heap = Heap::the(); - assert(size > 0 && "Heap: Cannot alloc less than 0B"); - if (heap.m_size + size > HEAP_SIZE) { - // try collect - collect(); - assert(heap.m_size + size <= HEAP_SIZE && "Heap: Out Of Memory"); + /** + * 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(__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) { + + // Singleton + Heap *heap = Heap::the(); + + if (size < 0) { + cout << "Heap: Cannot alloc less than 0B. No bytes allocated." << endl; + return nullptr; } - // kolla freed chunks innan - // denna är helt onödig just nu, freed chunks kommer alltid va tom - for (size_t i = 0; i < m_freed_chunks.size(); i++) { - auto cp = m_freed_chunks.at(i); - 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; + if (heap->m_size + size > HEAP_SIZE) { + collect(); + // If collect failed, crash with OOM error + assert(heap->m_size + size <= HEAP_SIZE && "Heap: Out Of Memory"); + } - 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; - } + // If a chunk was recycled, return the old chunk address + uintptr_t *reused_chunk = try_recycle_chunks(size); + if (reused_chunk != nullptr) { + return (void *)reused_chunk; } - // 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; new_chunk->size = size; new_chunk->start = (uintptr_t *)m_heap + m_size; @@ -63,32 +68,55 @@ namespace GC { return new_chunk->start; } - void Heap::collect() { + 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() { + // 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(__builtin_frame_address(0)); - // looking at 10 stack frames back - const uintptr_t *stack_end = (uintptr_t *)0; // temporary - - // denna segfaultar om arg för __b_f_a är > 2 - // reinterpret_cast(__builtin_frame_address(10)); + if (heap->m_stack_end != nullptr) + stack_end = heap->m_stack_end; + else + stack_end = (uintptr_t *)0; // temporary + auto work_list = m_allocated_chunks; mark(stack_start, stack_end, work_list); sweep(); - // compact(); - 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() { @@ -200,50 +228,6 @@ namespace GC { } } -/* void mark_test(vector 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 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 void Heap::print_line(Chunk *chunk) { cout << "Marked: " << chunk->marked << "\nStart adr: " << chunk->start << "\nSize: " << chunk->size << " B\n" << endl; diff --git a/src/GC/todo.md b/src/GC/todo.md index 3386d65..009ab51 100644 --- a/src/GC/todo.md +++ b/src/GC/todo.md @@ -7,7 +7,7 @@ Goal for next week (24/2): ## GC TODO: - Merge to main branch -- Don't empty m_free_chunks, reuse in a better way **Victor fixes this** +- Fix singleton references ## Tests TODO ### Library linking