From cdc802476d1ed9134112a4cbe901f235bb84d30e Mon Sep 17 00:00:00 2001 From: Victor Olin Date: Wed, 8 Mar 2023 16:47:34 +0100 Subject: [PATCH] Code cleanup --- src/GC/include/chunk.hpp | 16 +- src/GC/include/event.hpp | 24 +- src/GC/include/heap.hpp | 153 ++++---- src/GC/include/profiler.hpp | 4 +- src/GC/lib/event.cpp | 2 - src/GC/lib/heap.cpp | 763 +++++++++++++++++++----------------- 6 files changed, 503 insertions(+), 459 deletions(-) diff --git a/src/GC/include/chunk.hpp b/src/GC/include/chunk.hpp index 7aa4fb7..49858a2 100644 --- a/src/GC/include/chunk.hpp +++ b/src/GC/include/chunk.hpp @@ -2,14 +2,14 @@ #include -#define CHUNK_LIST_CAP 1024 +namespace GC +{ -namespace GC { - - struct Chunk { - bool marked; - uintptr_t *start; - size_t size; - }; + struct Chunk + { + bool marked; + uintptr_t *start; + size_t size; + }; } \ No newline at end of file diff --git a/src/GC/include/event.hpp b/src/GC/include/event.hpp index 9c03cd6..547e2aa 100644 --- a/src/GC/include/event.hpp +++ b/src/GC/include/event.hpp @@ -6,11 +6,11 @@ #include "chunk.hpp" -using namespace std; +namespace GC +{ -namespace GC { - - enum GCEventType { + enum GCEventType + { CollectStart, MarkStart, ChunkMarked, @@ -20,23 +20,27 @@ namespace GC { ReusedChunk }; - using TimeStamp = chrono::_V2::system_clock::time_point; + using TimeStamp = std::chrono::_V2::system_clock::time_point; - class GCEvent { + class GCEvent + { private: // make const GCEventType m_type; TimeStamp m_timestamp; Chunk *m_chunk; + public: - GCEvent(GCEventType type) { + GCEvent(GCEventType type) + { m_type = type; - m_timestamp = chrono::system_clock::now(); + m_timestamp = std::chrono::system_clock::now(); } - GCEvent(GCEventType type, Chunk *chunk) { + GCEvent(GCEventType type, Chunk *chunk) + { m_type = type; - m_timestamp = chrono::system_clock::now(); + m_timestamp = std::chrono::system_clock::now(); m_chunk = chunk; } diff --git a/src/GC/include/heap.hpp b/src/GC/include/heap.hpp index 2338878..24b307a 100644 --- a/src/GC/include/heap.hpp +++ b/src/GC/include/heap.hpp @@ -8,91 +8,96 @@ #include "chunk.hpp" -#define HEAP_SIZE 65536 +#define HEAP_SIZE 65536 -#define MARK (uint) 0x1 -#define SWEEP (uint) 0x2 -#define FREE (uint) 0x4 -#define COLLECT_ALL (uint) 0x7 +#define MARK (uint)0x1 +#define SWEEP (uint)0x2 +#define FREE (uint)0x4 +#define COLLECT_ALL (uint)0x7 -#define FREE_THRESH (uint) 20 +#define FREE_THRESH (uint)20 -namespace GC { +namespace GC +{ - class Heap { + class Heap + { - private: + private: + // Private constructor according to the singleton pattern + Heap() + { + m_heap = static_cast(malloc(HEAP_SIZE)); + m_size = 0; + m_allocated_size = 0; + } - //Private constructor according to the singleton pattern - Heap() { - m_heap = reinterpret_cast(malloc(HEAP_SIZE)); - m_size = 0; - m_allocated_size = 0; - } + // BEWARE only for testing, this should be adressed + ~Heap() + { + std::free((char *)m_heap); + } - // BEWARE only for testing, this should be adressed - ~Heap() { - std::free((char *)m_heap); - } + inline static Heap *the() + { // TODO: make private + if (m_instance) // if m_instance is not a nullptr + return m_instance; + m_instance = new Heap(); + return m_instance; + } - inline static Heap *the() { // TODO: make private - if (m_instance) // if m_instance is not a nullptr - return m_instance; - m_instance = new Heap(); - return m_instance; - } + inline static Chunk *getAt(std::vector &list, size_t n) + { + auto iter = list.begin(); + if (!n) + return *iter; + std::advance(iter, n); + return *iter; + } - inline static Chunk *getAt(std::vector list, size_t n) { - auto iter = list.begin(); - if (!n) - return *iter; - std::advance(iter, n); - return *iter; - } + void collect(); + void sweep(Heap *heap); + uintptr_t *try_recycle_chunks(size_t size); + void free(Heap *heap); + void free_overlap(Heap *heap); + void mark(uintptr_t *start, const uintptr_t *end, std::vector &worklist); + void print_line(Chunk *chunk); + void print_worklist(std::vector &list); - void collect(); - void sweep(Heap *heap); - uintptr_t *try_recycle_chunks(size_t size); - void free(Heap* heap); - void free_overlap(Heap *heap); - void mark(uintptr_t *start, const uintptr_t *end, std::vector worklist); - void print_line(Chunk *chunk); - void print_worklist(std::vector list); + inline static Heap *m_instance = nullptr; + const char *m_heap; + size_t m_size; + size_t m_allocated_size; + uintptr_t *m_stack_top = nullptr; + bool m_profiler_enable = false; - inline static Heap *m_instance = nullptr; - const char *m_heap; - size_t m_size; - size_t m_allocated_size; - uintptr_t *m_stack_top = nullptr; - bool m_profiler_enable = false; + // maybe change to std::list + std::vector m_allocated_chunks; + std::vector m_freed_chunks; - // maybe change to std::list - std::vector m_allocated_chunks; - std::vector m_freed_chunks; + public: + /** + * These are the only two functions which are exposed + * as the API for LLVM. At the absolute start of the + * program the developer has to call init() to ensure + * that the address of the topmost stack frame is + * saved as the limit for scanning the stack in collect. + */ + static void init(); + static void dispose(); + static void *alloc(size_t size); - public: - - /** - * These are the only two functions which are exposed - * as the API for LLVM. At the absolute start of the - * program the developer has to call init() to ensure - * that the address of the topmost stack frame is - * saved as the limit for scanning the stack in collect. - */ - static void init(); - static void dispose(); - static void *alloc(size_t size); - - // DEBUG ONLY - static inline Heap *debug_the() { // TODO: make private - if (m_instance) // if m_instance is not a nullptr - return m_instance; - m_instance = new Heap(); - return m_instance; - } - void collect(uint flags); // conditional collection - void check_init(); // print dummy things - void print_contents(); // print dummy things - void set_profiler(bool mode); - }; + // DEBUG ONLY + static inline Heap *debug_the() + { // TODO: make private + if (m_instance) // if m_instance is not a nullptr + return m_instance; + m_instance = new Heap(); + return m_instance; + } + void collect(uint flags); // conditional collection + void check_init(); // print dummy things + void print_contents(); // print dummy things + void set_profiler(bool mode); + }; } \ No newline at end of file diff --git a/src/GC/include/profiler.hpp b/src/GC/include/profiler.hpp index 7e305e4..a253fa6 100644 --- a/src/GC/include/profiler.hpp +++ b/src/GC/include/profiler.hpp @@ -6,8 +6,6 @@ #include "event.hpp" #include "heap.hpp" -using namespace std; - namespace GC { class Profiler { @@ -23,7 +21,7 @@ namespace GC { } inline static Profiler *m_instance = nullptr; - vector m_events; + std::vector m_events; public: static void record(GCEventType type); diff --git a/src/GC/lib/event.cpp b/src/GC/lib/event.cpp index b4b62c6..7c95a44 100644 --- a/src/GC/lib/event.cpp +++ b/src/GC/lib/event.cpp @@ -6,8 +6,6 @@ #include "event.hpp" #include "heap.hpp" -// using namespace std; - namespace GC { GCEventType GCEvent::getType() { diff --git a/src/GC/lib/heap.cpp b/src/GC/lib/heap.cpp index 69f25a8..9618415 100644 --- a/src/GC/lib/heap.cpp +++ b/src/GC/lib/heap.cpp @@ -8,405 +8,444 @@ #include #include "../include/heap.hpp" -using namespace std; -namespace GC { +using std::cout, std::endl, std::vector, std::hex, std::dec; - /** - * Initialises the heap singleton and saves the address - * of the calling stack frame as the stack_top. Presumeably - * this address points to the stack frame of the compiled - * LLVM executable after linking. - */ - void Heap::init() { - Heap *heap = Heap::the(); - heap->m_stack_top = reinterpret_cast(__builtin_frame_address(1)); - } +namespace GC +{ - /** - * Disposes the heap at program exit. - */ - void Heap::dispose() { - Heap *heap = Heap::the(); - delete heap; - } + /** + * Initialises the heap singleton and saves the address + * of the calling stack frame as the stack_top. Presumeably + * this address points to the stack frame of the compiled + * LLVM executable after linking. + */ + void Heap::init() + { + Heap *heap = Heap::the(); + heap->m_stack_top = static_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) { + /** + * Disposes the heap at program exit. + */ + void Heap::dispose() + { + Heap *heap = Heap::the(); + delete heap; + } - // Singleton - Heap *heap = Heap::the(); - - if (size < 0) { - cout << "Heap: Cannot alloc less than 0B. No bytes allocated." << endl; - return nullptr; - } + /** + * 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) + { - if (heap->m_size + size > HEAP_SIZE) { - heap->collect(); - // If collect failed, crash with OOM error - assert(heap->m_size + size <= HEAP_SIZE && "Heap: Out Of Memory"); - } + // Singleton + Heap *heap = Heap::the(); - // If a chunk was recycled, return the old chunk address - uintptr_t *reused_chunk = heap->try_recycle_chunks(size); - if (reused_chunk != nullptr) { - return (void *)reused_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 *)(heap->m_heap + heap->m_size); + if (size < 0) + { + cout << "Heap: Cannot alloc less than 0B. No bytes allocated." << endl; + return nullptr; + } - heap->m_size += size; + if (heap->m_size + size > HEAP_SIZE) + { + heap->collect(); + // If collect failed, crash with OOM error + assert(heap->m_size + size <= HEAP_SIZE && "Heap: Out Of Memory"); + } - heap->m_allocated_chunks.push_back(new_chunk); + // If a chunk was recycled, return the old chunk address + uintptr_t *reused_chunk = heap->try_recycle_chunks(size); + if (reused_chunk != nullptr) + { + return static_cast(reused_chunk); + } - // new_chunk should probably be a unique pointer, if that isn't implicit already - return new_chunk->start; - } + // 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 *)(heap->m_heap + heap->m_size); - /** - * Tries to recycle used and freed chunks that are - * already allocated objects by the OS but freed - * from our Heap. This reduces the amount of GC - * objects slightly which saves time from malloc'ing - * memory from the OS. - * - * @param size Amount of bytes needed for the object - * which is about to be allocated. - * - * @returns If a chunk is found and recycled, a - * pointer to the allocated memory for - * the object is returned. If not, a - * nullptr is returned to signify no - * chunks were found. - */ - 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); - auto cp = getAt(heap->m_freed_chunks, i); - auto iter = heap->m_freed_chunks.begin(); - advance(iter, 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_size += size; - heap->m_freed_chunks.erase(iter); - 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 - heap->m_freed_chunks.erase(iter); - heap->m_allocated_chunks.push_back(cp); - return cp->start; - } - } - return nullptr; - } + heap->m_allocated_chunks.push_back(new_chunk); - /** - * Collection phase of the garbage collector. When - * an allocation is requested and there is no space - * left on the heap, a collection is triggered. This - * function is private so that the user cannot trigger - * a collection unneccessarily. - */ - void Heap::collect() { - // Get instance - auto heap = Heap::the(); + // new_chunk should probably be a unique pointer, if that isn't implicit already + return new_chunk->start; + } - // get current stack - auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); + /** + * Tries to recycle used and freed chunks that are + * already allocated objects by the OS but freed + * from our Heap. This reduces the amount of GC + * objects slightly which saves time from malloc'ing + * memory from the OS. + * + * @param size Amount of bytes needed for the object + * which is about to be allocated. + * + * @returns If a chunk is found and recycled, a + * pointer to the allocated memory for + * the object is returned. If not, a + * nullptr is returned to signify no + * chunks were found. + */ + 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 chunk = getAt(heap->m_freed_chunks, i); + auto iter = heap->m_freed_chunks.begin(); + advance(iter, i); + if (chunk->size > size) + { + // Split the chunk, use one part and add the remaining part to + // the list of freed chunks + size_t diff = chunk->size - size; - uintptr_t *stack_top = heap->m_stack_top != nullptr ? heap->m_stack_top : (uintptr_t *)0; + auto chunk_complement = new Chunk; + chunk_complement->size = diff; + chunk_complement->start = chunk->start + chunk->size; - auto work_list = heap->m_allocated_chunks; - mark(stack_bottom, stack_top, work_list); + heap->m_freed_chunks.erase(iter); + heap->m_freed_chunks.push_back(chunk_complement); + heap->m_allocated_chunks.push_back(chunk); - sweep(heap); + return chunk->start; + } + else if (chunk->size == size) + { + // Reuse the whole chunk + heap->m_freed_chunks.erase(iter); + heap->m_allocated_chunks.push_back(chunk); + return chunk->start; + } + } + return nullptr; + } - free(heap); - } + /** + * Collection phase of the garbage collector. When + * an allocation is requested and there is no space + * left on the heap, a collection is triggered. This + * function is private so that the user cannot trigger + * a collection unneccessarily. + */ + void Heap::collect() + { + // Get instance + auto heap = Heap::the(); - /** - * Iterates through the stack, if an element on the stack points to a chunk, - * called a root chunk, that chunk is marked (i.e. reachable). - * Then it recursively follows all chunks which are possibly reachable from - * the root chunk and mark those chunks. - * If a chunk is marked it is removed from the worklist, since it's no longer of - * concern for this method. - * - * @param start Pointer to the start of the stack frame. - * @param end Pointer to the end of the stack frame. - * @param worklist The currently allocated chunks, which haven't been marked. - */ - void Heap::mark(uintptr_t *start, const uintptr_t *end, vector worklist) { - int counter = 0; - // To find adresses thats in the worklist - for (; start < end; start++) { - counter++; - auto it = worklist.begin(); - auto stop = worklist.end(); - // for (auto it = worklist.begin(); it != worklist.end();) { - while (it != stop) { - Chunk *chunk = *it; + // get current stack + auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); - auto c_start = reinterpret_cast(chunk->start); - auto c_size = reinterpret_cast(chunk->size); - auto c_end = reinterpret_cast(c_start + c_size); + uintptr_t *stack_top = heap->m_stack_top != nullptr ? heap->m_stack_top : (uintptr_t *)0; - cout << "Start points to:\t" << hex << *start << endl; - cout << "Chunk start:\t\t" << hex << c_start << endl; - cout << "Chunk end:\t\t" << hex << c_end << "\n" << endl; + auto work_list = heap->m_allocated_chunks; + mark(stack_bottom, stack_top, work_list); - // Check if the stack pointer aligns with the chunk - if (c_start <= *start && *start < c_end) { - - if (!chunk->marked) { - chunk->marked = true; - // Remove the marked chunk from the worklist - it = worklist.erase(it); - // Recursively call mark, to see if the reachable chunk further points to another chunk - mark((uintptr_t*) c_start, (uintptr_t*) c_end, worklist); - } - else { - ++it; - } - } - else { - ++it; - } - } - } - cout << "Counter: " << counter << endl; - } - - /** - * Sweeps the heap, unmarks the marked chunks for the next cycle, - * adds the unmarked nodes to the list of freed chunks; to be freed. - * - * @param heap Pointer to the heap singleton instance. - */ - void Heap::sweep(Heap *heap) { - auto iter = heap->m_allocated_chunks.begin(); - auto stop = heap->m_allocated_chunks.end(); - // for (auto it = heap->m_allocated_chunks.begin(); it != heap->m_allocated_chunks.end();) { - while (iter != stop) { - Chunk *chunk = *iter; + sweep(heap); - // Unmark the marked chunks for the next iteration. - if (chunk->marked) { - chunk->marked = false; - ++iter; - } - else { - // Add the unmarked chunks to freed chunks and remove from - // the list of allocated chunks - heap->m_freed_chunks.push_back(chunk); - iter = heap->m_allocated_chunks.erase(iter); - } - } - } + free(heap); + } - /** - * Frees chunks that was moved to the list m_freed_chunks - * by the sweep phase. If there are more than a certain - * amount of free chunks, delete the free chunks to - * avoid cluttering. - * - * @param heap Heap singleton instance, only for avoiding - * redundant calls to the singleton get - */ - void Heap::free(Heap *heap) { - if (heap->m_freed_chunks.size() > FREE_THRESH) { - while (heap->m_freed_chunks.size()) { - auto chunk = heap->m_freed_chunks.back(); - heap->m_freed_chunks.pop_back(); - delete chunk; - } - } - // if there are chunks but not more than FREE_THRESH - else if (heap->m_freed_chunks.size()) { - // essentially, always check for overlap between - // chunks before finishing the allocation - free_overlap(heap); - } - } + /** + * Iterates through the stack, if an element on the stack points to a chunk, + * called a root chunk, that chunk is marked (i.e. reachable). + * Then it recursively follows all chunks which are possibly reachable from + * the root chunk and mark those chunks. + * If a chunk is marked it is removed from the worklist, since it's no longer of + * concern for this method. + * + * @param start Pointer to the start of the stack frame. + * @param end Pointer to the end of the stack frame. + * @param worklist The currently allocated chunks, which haven't been marked. + */ + void Heap::mark(uintptr_t *start, const uintptr_t *end, vector &worklist) + { + int counter = 0; + // To find adresses thats in the worklist + for (; start < end; start++) + { + counter++; + auto it = worklist.begin(); + auto stop = worklist.end(); + // for (auto it = worklist.begin(); it != worklist.end();) { + while (it != stop) + { + Chunk *chunk = *it; - /** - * Checks for overlaps between freed chunks of memory - * and removes overlapping chunks while prioritizing - * the chunks at lower addresses. - * - * @param heap Heap singleton instance, only for avoiding - * redundant calls to the singleton get - * - * @note Maybe this should be changed to prioritizing - * larger chunks. - */ - void Heap::free_overlap(Heap *heap) { - std::vector filtered; - size_t i = 0; - // filtered.push_back(heap->m_freed_chunks.at(i++)); - filtered.push_back(getAt(heap->m_freed_chunks, i++)); - cout << filtered.back()->start << endl; - for (; i < heap->m_freed_chunks.size(); i++) { - auto prev = filtered.back(); - // auto next = heap->m_freed_chunks.at(i); - auto next = getAt(heap->m_freed_chunks, i); - auto p_start = (uintptr_t)(prev->start); - auto p_size = (uintptr_t)(prev->size); - auto n_start = (uintptr_t)(next->start); - if (n_start >= (p_start + p_size)) { - filtered.push_back(next); - } - } - heap->m_freed_chunks.swap(filtered); - } + auto c_start = reinterpret_cast(chunk->start); + auto c_size = reinterpret_cast(chunk->size); + auto c_end = reinterpret_cast(c_start + c_size); - // ----- ONLY DEBUGGING ----------------------------------------------------------------------- + cout << "Start points to:\t" << hex << *start << endl; + cout << "Chunk start:\t\t" << hex << c_start << endl; + cout << "Chunk end:\t\t" << hex << c_end << "\n" + << endl; - /** - * Prints the result of Heap::init() and a dummy value - * for the current stack frame for reference. - */ - void Heap::check_init() { - auto heap = Heap::the(); - cout << "Heap addr:\t" << heap << endl; - cout << "GC m_stack_top:\t" << heap->m_stack_top << endl; - auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); - cout << "GC stack_bottom:\t" << stack_bottom << endl; - } - - /** - * Conditional collection, only to be used in debugging - * - * @param flags Bitmap of flags - */ - void Heap::collect(uint flags) { + // Check if the stack pointer aligns with the chunk + if (c_start <= *start && *start < c_end) + { - cout << "DEBUG COLLECT\nFLAGS: "; - if (flags & MARK) - cout << "\n - MARK"; - if (flags & SWEEP) - cout << "\n - SWEEP"; - if (flags & FREE) - cout << "\n - FREE"; - cout << endl; - - auto heap = Heap::the(); + if (!chunk->marked) + { + chunk->marked = true; + // Remove the marked chunk from the worklist + it = worklist.erase(it); + // Recursively call mark, to see if the reachable chunk further points to another chunk + mark((uintptr_t *)c_start, (uintptr_t *)c_end, worklist); + } + else + { + ++it; + } + } + else + { + ++it; + } + } + } + cout << "Counter: " << counter << endl; + } - // get the frame adress, whwere local variables and saved registers are located - auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); - cout << "Stack bottom in collect:\t" << stack_bottom << endl; - uintptr_t *stack_top; + /** + * Sweeps the heap, unmarks the marked chunks for the next cycle, + * adds the unmarked nodes to the list of freed chunks; to be freed. + * + * @param heap Pointer to the heap singleton instance. + */ + void Heap::sweep(Heap *heap) + { + auto iter = heap->m_allocated_chunks.begin(); + auto stop = heap->m_allocated_chunks.end(); + // for (auto it = heap->m_allocated_chunks.begin(); it != heap->m_allocated_chunks.end();) { + while (iter != stop) + { + Chunk *chunk = *iter; - if (heap->m_stack_top != nullptr) - stack_top = heap->m_stack_top; - else - stack_top = (uintptr_t *) stack_bottom + 80; // dummy value + // Unmark the marked chunks for the next iteration. + if (chunk->marked) + { + chunk->marked = false; + ++iter; + } + else + { + // Add the unmarked chunks to freed chunks and remove from + // the list of allocated chunks + heap->m_freed_chunks.push_back(chunk); + iter = heap->m_allocated_chunks.erase(iter); + } + } + } - cout << "Stack end in collect:\t " << stack_top << endl; - auto work_list = heap->m_allocated_chunks; + /** + * Frees chunks that was moved to the list m_freed_chunks + * by the sweep phase. If there are more than a certain + * amount of free chunks, delete the free chunks to + * avoid cluttering. + * + * @param heap Heap singleton instance, only for avoiding + * redundant calls to the singleton get + */ + void Heap::free(Heap *heap) + { + if (heap->m_freed_chunks.size() > FREE_THRESH) + { + while (heap->m_freed_chunks.size()) + { + auto chunk = heap->m_freed_chunks.back(); + heap->m_freed_chunks.pop_back(); + delete chunk; + } + } + // if there are chunks but not more than FREE_THRESH + else if (heap->m_freed_chunks.size()) + { + // essentially, always check for overlap between + // chunks before finishing the allocation + free_overlap(heap); + } + } - if (flags & MARK) { - mark(stack_bottom, stack_top, work_list); - } + /** + * Checks for overlaps between freed chunks of memory + * and removes overlapping chunks while prioritizing + * the chunks at lower addresses. + * + * @param heap Heap singleton instance, only for avoiding + * redundant calls to the singleton get + * + * @note Maybe this should be changed to prioritizing + * larger chunks. + */ + void Heap::free_overlap(Heap *heap) + { + std::vector filtered; + size_t i = 0; + // filtered.push_back(heap->m_freed_chunks.at(i++)); + filtered.push_back(getAt(heap->m_freed_chunks, i++)); + cout << filtered.back()->start << endl; + for (; i < heap->m_freed_chunks.size(); i++) + { + auto prev = filtered.back(); + // auto next = heap->m_freed_chunks.at(i); + auto next = getAt(heap->m_freed_chunks, i); + auto p_start = (uintptr_t)(prev->start); + auto p_size = (uintptr_t)(prev->size); + auto n_start = (uintptr_t)(next->start); + if (n_start >= (p_start + p_size)) + { + filtered.push_back(next); + } + } + heap->m_freed_chunks.swap(filtered); + } - if (flags & SWEEP) { - sweep(heap); - } - - if (flags & FREE) { - free(heap); - } - } + // ----- ONLY DEBUGGING ----------------------------------------------------------------------- - // Mark child references from the root references - void mark_test(vector worklist) { - while (worklist.size() > 0) { - Chunk *ref = worklist.back(); - worklist.pop_back(); - Chunk *child = (Chunk*) ref; // this is probably not correct - if (child != nullptr && !child->marked) { - child->marked = true; - worklist.push_back(child); - mark_test(worklist); - } - } - } + /** + * Prints the result of Heap::init() and a dummy value + * for the current stack frame for reference. + */ + void Heap::check_init() + { + auto heap = Heap::the(); + cout << "Heap addr:\t" << heap << "\n"; + cout << "GC m_stack_top:\t" << heap->m_stack_top << "\n"; + auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); + cout << "GC stack_bottom:\t" << stack_bottom << endl; + } - // Mark the root references and look for child references to them - void mark_from_roots(uintptr_t *start, const uintptr_t *end) { - vector worklist; - for (;start > end; start --) { - if (*start % 8 == 0) { // all pointers must be aligned as double words - Chunk *ref = (Chunk*) *start; - if (ref != nullptr && !ref->marked) { - ref->marked = true; - worklist.push_back(ref); - mark_test(worklist); - } - } - } - } + /** + * Conditional collection, only to be used in debugging + * + * @param flags Bitmap of flags + */ + void Heap::collect(uint flags) + { - // For testing purposes - void Heap::print_line(Chunk *chunk) { - cout << "Marked: " << chunk->marked << "\nStart adr: " << chunk->start << "\nSize: " << chunk->size << " B\n" << endl; - } + cout << "DEBUG COLLECT\nFLAGS: "; + if (flags & MARK) + cout << "\n - MARK"; + if (flags & SWEEP) + cout << "\n - SWEEP"; + if (flags & FREE) + cout << "\n - FREE"; + cout << "\n"; - void Heap::print_worklist(std::vector list) { - for (auto cp : list) { - cout << "Chunk at:\t" << cp->start << "\nSize:\t\t" << cp->size << endl; - } - } + auto heap = Heap::the(); - void Heap::print_contents() { - auto heap = Heap::the(); - if (heap->m_allocated_chunks.size()) { - cout << "\nALLOCATED CHUNKS #" << dec << heap->m_allocated_chunks.size() << endl; - for (auto chunk : heap->m_allocated_chunks) { - print_line(chunk); - } - } else { - cout << "NO ALLOCATIONS\n" << endl; - } - if (heap->m_freed_chunks.size()) { - cout << "\nFREED CHUNKS #" << dec << heap->m_freed_chunks.size() << endl; - for (auto fchunk : heap->m_freed_chunks) { - print_line(fchunk); - } - } else { - cout << "NO FREED CHUNKS" << endl; - } - } + // get the frame adress, whwere local variables and saved registers are located + auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); + cout << "Stack bottom in collect:\t" << stack_bottom << "\n"; + uintptr_t *stack_top = heap->m_stack_top; - void Heap::set_profiler(bool mode) { - auto heap = Heap::the(); - heap->m_profiler_enable = mode; - } + cout << "Stack end in collect:\t " << stack_top << endl; + auto work_list = heap->m_allocated_chunks; + + if (flags & MARK) + mark(stack_bottom, stack_top, work_list); + + if (flags & SWEEP) + sweep(heap); + + if (flags & FREE) + free(heap); + } + + // Mark child references from the root references + void mark_test(vector &worklist) + { + while (worklist.size() > 0) + { + Chunk *ref = worklist.back(); + worklist.pop_back(); + Chunk *child = (Chunk *)ref; // this is probably not correct + if (child != nullptr && !child->marked) + { + child->marked = true; + worklist.push_back(child); + mark_test(worklist); + } + } + } + + // Mark the root references and look for child references to them + void mark_from_roots(uintptr_t *start, const uintptr_t *end) + { + vector worklist; + for (; start > end; start--) + { + if (*start % 8 == 0) + { // all pointers must be aligned as double words + Chunk *ref = (Chunk *)*start; + if (ref != nullptr && !ref->marked) + { + ref->marked = true; + worklist.push_back(ref); + mark_test(worklist); + } + } + } + } + + // For testing purposes + void Heap::print_line(Chunk *chunk) + { + cout << "Marked: " << chunk->marked << "\nStart adr: " << chunk->start << "\nSize: " << chunk->size << " B\n" + << endl; + } + + void Heap::print_worklist(std::vector &list) + { + for (auto cp : list) + cout << "Chunk at:\t" << cp->start << "\nSize:\t\t" << cp->size << "\n"; + cout << endl; + } + + void Heap::print_contents() + { + auto heap = Heap::the(); + if (heap->m_allocated_chunks.size()) + { + cout << "\nALLOCATED CHUNKS #" << dec << heap->m_allocated_chunks.size() << endl; + for (auto chunk : heap->m_allocated_chunks) + print_line(chunk); + } + else + { + cout << "NO ALLOCATIONS\n" << endl; + } + if (heap->m_freed_chunks.size()) + { + cout << "\nFREED CHUNKS #" << dec << heap->m_freed_chunks.size() << endl; + for (auto fchunk : heap->m_freed_chunks) + print_line(fchunk); + } + else + { + cout << "NO FREED CHUNKS" << endl; + } + } + + void Heap::set_profiler(bool mode) + { + auto heap = Heap::the(); + heap->m_profiler_enable = mode; + } } \ No newline at end of file