From a388f480e59bb6b14e91cbf41c23c8ec64b75d47 Mon Sep 17 00:00:00 2001 From: Samuel Hammersberg Date: Fri, 5 May 2023 18:50:05 +0200 Subject: [PATCH] Hooked the GC back in B) --- sample-programs/loop.crf | 8 +- src/Codegen/Emits.hs | 8 +- src/GC/Makefile | 46 ++- src/GC/docs/lib/cheap.md | 40 +++ src/GC/include/cheap.h | 37 ++ src/GC/include/event.hpp | 25 +- src/GC/include/heap.hpp | 38 +- src/GC/include/profiler.hpp | 56 ++- src/GC/lib/cheap.cpp | 63 ++++ src/GC/lib/event.cpp | 4 - src/GC/lib/heap.cpp | 340 ++++++++++++++---- src/GC/lib/profiler.cpp | 213 ++++++++--- src/GC/tests/advance.cpp | 89 +++-- src/GC/tests/alloc_free_list.cpp | 250 +++++++++++++ src/GC/tests/file.cpp | 11 +- src/GC/tests/h_test.cpp | 12 +- .../tests/h_test.out.dSYM/Contents/Info.plist | 20 ++ src/GC/tests/linkedlist.cpp | 116 ++++++ src/GC/tests/linker.cpp | 16 +- src/GC/tests/pointers.cpp | 39 ++ src/GC/tests/revrange.cpp | 43 +++ src/GC/tests/wrapper.c | 96 +++++ src/GC/tests/wrapper_test.c | 53 +++ src/Main.hs | 8 +- 24 files changed, 1404 insertions(+), 227 deletions(-) create mode 100644 src/GC/docs/lib/cheap.md create mode 100644 src/GC/include/cheap.h create mode 100644 src/GC/lib/cheap.cpp create mode 100644 src/GC/tests/alloc_free_list.cpp create mode 100644 src/GC/tests/h_test.out.dSYM/Contents/Info.plist create mode 100644 src/GC/tests/linkedlist.cpp create mode 100644 src/GC/tests/pointers.cpp create mode 100644 src/GC/tests/revrange.cpp create mode 100644 src/GC/tests/wrapper.c create mode 100644 src/GC/tests/wrapper_test.c diff --git a/sample-programs/loop.crf b/sample-programs/loop.crf index e3c3c38..f80599e 100644 --- a/sample-programs/loop.crf +++ b/sample-programs/loop.crf @@ -2,7 +2,7 @@ main = for 0 1000 for x n = case n of 0 => 0 - n => for (revRange 1000) (n + minusOne) + n => for (revRange 1000) (n - 1) data List (a) where Nil : List (a) @@ -11,8 +11,4 @@ data List (a) where -- create a list of x to 0 revRange x = case x of 0 => Cons x Nil - x => Cons x (revRange (x + minusOne)) - --- represents minus one :) -minusOne : Int ; -minusOne = 9223372036854775807 + 9223372036854775807 + 1; \ No newline at end of file + x => Cons x (revRange (x - 1)) \ No newline at end of file diff --git a/src/Codegen/Emits.hs b/src/Codegen/Emits.hs index bc19f87..f3da73a 100644 --- a/src/Codegen/Emits.hs +++ b/src/Codegen/Emits.hs @@ -129,10 +129,10 @@ compileScs (MIR.DData (MIR.Data typ ts) : xs) = do firstMainContent :: Bool -> [LLVMIr] firstMainContent True = - [ UnsafeRaw "%prof = call ptr @cheap_the()\n" - , UnsafeRaw "call void @cheap_set_profiler(ptr %prof, i1 true)\n" - , UnsafeRaw "call void @cheap_profiler_log_options(ptr %prof, i64 30)\n" - , UnsafeRaw "call void @cheap_init()\n" + [ -- UnsafeRaw "%prof = call ptr @cheap_the()\n" + -- , UnsafeRaw "call void @cheap_set_profiler(ptr %prof, i1 true)\n" + -- , UnsafeRaw "call void @cheap_profiler_log_options(ptr %prof, i64 30)\n" + UnsafeRaw "call void @cheap_init()\n" ] firstMainContent False = [] diff --git a/src/GC/Makefile b/src/GC/Makefile index add6d73..4a852a0 100644 --- a/src/GC/Makefile +++ b/src/GC/Makefile @@ -18,7 +18,7 @@ file: heap: $(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) lib/heap.cpp -h_test: +h_test: static_lib rm -f tests/h_test.out # $(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) tests/h_test.cpp lib/heap.cpp lib/profiler.cpp lib/event.cpp -o tests/h_test.out $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -g -o tests/h_test.out tests/h_test.cpp lib/gcoll.a @@ -40,6 +40,17 @@ game: rm -f tests/game.out $(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) tests/game.cpp lib/heap.cpp lib/profiler.cpp lib/event.cpp -o tests/game.out +wrapper_test: + rm -f lib/event.o lib/profiler.o lib/heap.o lib/coll.a tests/wrapper_test.out +# compile object files + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/event.o lib/event.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/profiler.o lib/profiler.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/heap.o lib/heap.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/cheap.o lib/cheap.cpp -fPIC +# compile object files into library + ar rcs lib/gcoll.a lib/event.o lib/profiler.o lib/heap.o lib/cheap.o + clang -stdlib=libc++ $(WFLAGS) $(LIB_INCL) -o tests/wrapper_test.out tests/wrapper_test.c lib/gcoll.a -lstdc++ + extern_lib: # remove old files rm -f lib/heap.o lib/libheap.so tests/extern_lib.out @@ -55,12 +66,37 @@ static_lib: # remove old files rm -f lib/event.o lib/profiler.o lib/heap.o lib/gcoll.a tests/extern_lib.out # compile object files - $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -g -c -o lib/event.o lib/event.cpp -fPIC - $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -g -c -o lib/profiler.o lib/profiler.cpp -fPIC - $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -g -c -o lib/heap.o lib/heap.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -c -o lib/event.o lib/event.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -c -o lib/profiler.o lib/profiler.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -c -o lib/heap.o lib/heap.cpp -fPIC # create static library ar r lib/gcoll.a lib/event.o lib/profiler.o lib/heap.o # create test program static_lib_test: static_lib - $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/extern_lib.out tests/extern_lib.cpp lib/gcoll.a \ No newline at end of file + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/extern_lib.out tests/extern_lib.cpp lib/gcoll.a + +alloc_free_list: static_lib + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/alloc_fl.out tests/alloc_free_list.cpp lib/gcoll.a + +linked_list_test: static_lib + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/linkedlist.out tests/linkedlist.cpp lib/gcoll.a + +revrange: static_lib + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/revrange.out tests/revrange.cpp lib/gcoll.a + +pointers: static_lib + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/pointers.out tests/pointers.cpp lib/gcoll.a + +wrapper: +# remove old files + rm -f lib/event.o lib/profiler.o lib/heap.o lib/coll.a tests/wrapper.out +# compile object files + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/event.o lib/event.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/profiler.o lib/profiler.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/heap.o lib/heap.cpp -fPIC + $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/cheap.o lib/cheap.cpp -fPIC +# compile object files into library + ar rcs lib/gcoll.a lib/event.o lib/profiler.o lib/heap.o lib/cheap.o +# compile test program wrapper.c with normal clang + clang -stdlib=libc++ $(WFLAGS) $(LIB_INCL) -o tests/wrapper.out tests/wrapper.c lib/gcoll.a -lstdc++ diff --git a/src/GC/docs/lib/cheap.md b/src/GC/docs/lib/cheap.md new file mode 100644 index 0000000..e5c5993 --- /dev/null +++ b/src/GC/docs/lib/cheap.md @@ -0,0 +1,40 @@ +# cheap.h & cheap.cpp + +A wrapper interface for the class `GC::Heap` for easier use +in LLVM (no nasty namespaces). This interface is relatively +straight-forward and only defines functions to use the already +public functions in the class `GC::Heap`. + +The functions are declared in a normal C-style header and +defined as "pure" C-functions. Because the public functions +exposed in `GC::Heap` are static, some of the functions +just call the static functions but are wrapped as C-functions. + +For the non-static function `GC::Heap::set_profiler()` and the +singleton get-instance function `GC::Heap::the()` a struct +is used to encapsulate the heap-object. If this library is +compiled with `DEBUG` defined a struct is typedef-ed and +can be used everywhere, otherwise this struct is opaque +and cannot be used explicitly. This struct only contains +a pointer to the heap instance and is called `cheap_t`. + +## Functions +`cheap_t *cheap_the()`: Returns an encapsulated singleton +instance. It is encapsulated in an opaque struct as the +instance itself is not meant to be used outside the C++ +library. + +`void cheap_init()`: Simply calls the `Heap::init()` +function. + +`void cheap_dispose()`: Only calls the `Heap::dispose()` +function. + +`void *cheap_alloc(unsigned long size)`: Calls `Heap::alloc(size_t size)` +and returns whatever `alloc` returns. + +`void cheap_set_profiler(cheap_t *cheap, bool mode)`: +The argument `cheap` is the encapsulated Heap singleton instance. +`mode` is the same as for `Heap::set_profiler(bool mode)`. + +For more documentation on functionality, see `src/GC/docs/lib/heap.md`. \ No newline at end of file diff --git a/src/GC/include/cheap.h b/src/GC/include/cheap.h new file mode 100644 index 0000000..84f5971 --- /dev/null +++ b/src/GC/include/cheap.h @@ -0,0 +1,37 @@ +#ifndef CHEAP_H +#define CHEAP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEBUG +#define WRAPPER_DEBUG + +#ifdef WRAPPER_DEBUG +typedef struct cheap +{ + void *obj; +} cheap_t; +#else +struct cheap; +typedef struct cheap cheap_t; +#endif + +#define FuncCallsOnly 0x1E +#define ChunkOpsOnly 0x3E0 + +cheap_t *cheap_the(); +void cheap_init(); +void cheap_dispose(); +void *cheap_alloc(unsigned long size); +void cheap_set_profiler(cheap_t *cheap, bool mode); +void cheap_profiler_log_options(cheap_t *cheap, unsigned long flag); + +#ifdef __cplusplus +} +#endif + +#endif /* __CHEAP_H__ */ \ No newline at end of file diff --git a/src/GC/include/event.hpp b/src/GC/include/event.hpp index 298ccab..c18b1ce 100644 --- a/src/GC/include/event.hpp +++ b/src/GC/include/event.hpp @@ -1,9 +1,6 @@ #pragma once #include -#include -#include -#include #include "chunk.hpp" @@ -14,16 +11,18 @@ namespace GC */ enum GCEventType { - HeapInit, - AllocStart, - CollectStart, - MarkStart, - ChunkMarked, - ChunkSwept, - ChunkFreed, - NewChunk, - ReusedChunk, - ProfilerDispose + HeapInit = 1 << 0, + AllocStart = 1 << 1, + CollectStart = 1 << 2, + MarkStart = 1 << 3, + SweepStart = 1 << 4, + ChunkMarked = 1 << 5, + ChunkSwept = 1 << 6, + ChunkFreed = 1 << 7, + NewChunk = 1 << 8, + ReusedChunk = 1 << 9, + ProfilerDispose = 1 << 10, + FreeStart = 1 << 11 }; /** diff --git a/src/GC/include/heap.hpp b/src/GC/include/heap.hpp index 365a838..c9eebf8 100644 --- a/src/GC/include/heap.hpp +++ b/src/GC/include/heap.hpp @@ -1,17 +1,16 @@ #pragma once -#include -#include -#include +#include #include #include +#include #include "chunk.hpp" #include "profiler.hpp" -#define HEAP_SIZE 2097152 //65536 -#define FREE_THRESH (uint) 100000 -#define DEBUG +#define HEAP_SIZE 320//65536 +#define FREE_THRESH (uint) 0 +// #define HEAP_DEBUG namespace GC { @@ -20,11 +19,18 @@ namespace GC * collection (mark/sweep/free/all). */ enum CollectOption { - MARK=0x1, - SWEEP=0x2, - MARK_SWEEP = 0x3, - FREE=0x4, - COLLECT_ALL=0x7 + MARK = 1 << 0, + SWEEP = 1 << 1, + MARK_SWEEP = 1 << 2, + FREE = 1 << 3, + COLLECT_ALL = 0b1111 // all flags above + }; + + struct AddrRange + { + const uintptr_t *start, *end; + + AddrRange(uintptr_t *_start, uintptr_t *_end) : start(_start), end(_end) {} }; /** @@ -53,6 +59,8 @@ namespace GC std::vector m_allocated_chunks; std::vector m_freed_chunks; + std::list m_free_list; + std::unordered_map m_chunk_table; static bool profiler_enabled(); // static Chunk *get_at(std::vector &list, size_t n); @@ -62,14 +70,17 @@ namespace GC void free(Heap &heap); void free_overlap(Heap &heap); void mark(uintptr_t *start, const uintptr_t *end, std::vector &worklist); + void mark_hash(uintptr_t *start, const uintptr_t *end); + Chunk* find_pointer_hash(uintptr_t *start, const uintptr_t *end); + void create_table(); void print_line(Chunk *chunk); void print_worklist(std::vector &list); void mark_step(uintptr_t start, uintptr_t end, std::vector &worklist); + void mark_range(std::vector &ranges, std::vector &worklist); // Temporary Chunk *try_recycle_chunks_new(size_t size); void free_overlap_new(Heap &heap); - public: /** * These are the only five functions which are exposed @@ -84,12 +95,13 @@ namespace GC static void dispose(); static void *alloc(size_t size); void set_profiler(bool mode); + void set_profiler_log_options(RecordOption flags); // Stop the compiler from generating copy-methods Heap(Heap const&) = delete; Heap& operator=(Heap const&) = delete; -#ifdef DEBUG +#ifdef HEAP_DEBUG void collect(CollectOption flags); // conditional collection void check_init(); // print dummy things void print_contents(); // print dummy things diff --git a/src/GC/include/profiler.hpp b/src/GC/include/profiler.hpp index ccdf463..1c44ef6 100644 --- a/src/GC/include/profiler.hpp +++ b/src/GC/include/profiler.hpp @@ -1,12 +1,32 @@ #pragma once +#include #include +#include #include "chunk.hpp" #include "event.hpp" +// #define FunctionCallTypes +// #define ChunkOpsTypes + namespace GC { + enum RecordOption + { + FunctionCalls = (GC::AllocStart | GC::CollectStart | GC::MarkStart | GC::SweepStart | GC::FreeStart), + ChunkOps = (GC::ChunkMarked | GC::ChunkSwept | GC::ChunkFreed | GC::NewChunk | GC::ReusedChunk), + AllOps = 0xFFFFFF + }; + + struct ProfilerEvent + { + uint m_n {1}; + const GCEventType m_type; + + ProfilerEvent(GCEventType type) : m_type(type) {} + }; + class Profiler { private: Profiler() {} @@ -16,34 +36,36 @@ namespace GC { delete c; } - /** - * Returns the instance of the Profiler singleton. - * If m_instance is the nullptr and the profiler - * is not initialized yet, initialize it and return - * the pointer to it. Otherwise return the previously - * initialized pointer. - * - * @returns The pointer to the profiler singleton. - */ - static Profiler *the() - { - if (m_instance) - return m_instance; - m_instance = new Profiler(); - return m_instance; - } - + static Profiler &the(); inline static Profiler *m_instance {nullptr}; std::vector m_events; + ProfilerEvent *m_last_prof_event {new ProfilerEvent(HeapInit)}; + std::vector m_prof_events; + RecordOption flags; + std::chrono::microseconds alloc_time {0}; + // size_t alloc_counts {0}; + std::chrono::microseconds collect_time {0}; + // size_t collect_counts {0}; + + static void record_data(GCEvent *type); std::ofstream create_file_stream(); std::string get_log_folder(); static void dump_trace(); + static void dump_prof_trace(); + static void dump_chunk_trace(); + // static void dump_trace_short(); + // static void dump_trace_full(); + static void print_chunk_event(GCEvent *event, char buffer[22]); + static const char *type_to_string(GCEventType type); public: + static RecordOption log_options(); + static void set_log_options(RecordOption flags); static void record(GCEventType type); static void record(GCEventType type, size_t size); static void record(GCEventType type, Chunk *chunk); + static void record(GCEventType type, std::chrono::microseconds time); static void dispose(); }; } \ No newline at end of file diff --git a/src/GC/lib/cheap.cpp b/src/GC/lib/cheap.cpp new file mode 100644 index 0000000..42179b6 --- /dev/null +++ b/src/GC/lib/cheap.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include "heap.hpp" +#include "cheap.h" + +#ifndef WRAPPER_DEBUG +struct cheap +{ + void *obj; +}; +#endif + +cheap_t *cheap_the() +{ + cheap_t *c; + GC::Heap *heap; + + c = static_cast(malloc(sizeof(cheap_t))); + heap = &GC::Heap::the(); + c->obj = heap; + + return c; +} + +void cheap_init() +{ + GC::Heap::init(); +} + +void cheap_dispose() +{ + std::cout << "In dispose\n"; + GC::Heap::dispose(); + std::cout << "Out dispose" << std::endl; +} + +void *cheap_alloc(unsigned long size) +{ + return GC::Heap::alloc(size); +} + +void cheap_set_profiler(cheap_t *cheap, bool mode) +{ + GC::Heap *heap = static_cast(cheap->obj); + + heap->set_profiler(mode); +} + +void cheap_profiler_log_options(cheap_t *cheap, unsigned long flags) +{ + GC::Heap *heap = static_cast(cheap->obj); + + GC::RecordOption cast_flag; + if (flags == FuncCallsOnly) + cast_flag = GC::FunctionCalls; + else if (flags == ChunkOpsOnly) + cast_flag = GC::ChunkOps; + else + cast_flag = GC::AllOps; + + heap->set_profiler_log_options(cast_flag); +} \ No newline at end of file diff --git a/src/GC/lib/event.cpp b/src/GC/lib/event.cpp index 2815a77..89a2a71 100644 --- a/src/GC/lib/event.cpp +++ b/src/GC/lib/event.cpp @@ -1,7 +1,3 @@ -#include -#include -#include - #include "chunk.hpp" #include "event.hpp" diff --git a/src/GC/lib/heap.cpp b/src/GC/lib/heap.cpp index 579f421..ee6a1ef 100644 --- a/src/GC/lib/heap.cpp +++ b/src/GC/lib/heap.cpp @@ -1,16 +1,16 @@ -#include -#include -#include -#include #include -#include #include #include #include +#include +#include #include "heap.hpp" -using std::cout, std::endl, std::vector, std::hex, std::dec; +#define time_now std::chrono::high_resolution_clock::now() +#define to_us std::chrono::duration_cast + +using std::cout, std::endl, std::vector, std::hex, std::dec, std::unordered_map; namespace GC { @@ -18,10 +18,10 @@ namespace GC * This implementation of the() guarantees laziness * on the instance and a correct destruction with * the destructor. - * + * * @returns The singleton object. - */ - Heap& Heap::the() + */ + Heap &Heap::the() { static Heap instance; return instance; @@ -41,6 +41,13 @@ namespace GC // clang complains because arg for __b_f_a is not 0 which is "unsafe" #pragma clang diagnostic ignored "-Wframe-address" heap.m_stack_top = static_cast(__builtin_frame_address(1)); + // TODO: handle this below + // heap.m_heap_top = heap.m_heap; + } + + void Heap::set_profiler_log_options(RecordOption flags) + { + Profiler::set_log_options(flags); } /** @@ -66,10 +73,11 @@ namespace GC */ void *Heap::alloc(size_t size) { + auto a_start = time_now; // Singleton Heap &heap = Heap::the(); bool profiler_enabled = heap.profiler_enabled(); - + if (profiler_enabled) Profiler::record(AllocStart, size); @@ -81,8 +89,20 @@ namespace GC if (heap.m_size + size > HEAP_SIZE) { + // auto a_ms = to_us(c_start - a_start); + // Profiler::record(AllocStart, a_ms); heap.collect(); // If memory is not enough after collect, crash with OOM error + if (heap.m_size > HEAP_SIZE) + { + throw std::runtime_error(std::string("Error: Heap out of memory")); + } + // throw std::runtime_error(std::string("Error: Heap out of memory")); + } + if (heap.m_size + size > HEAP_SIZE) + { + if (profiler_enabled) + Profiler::dispose(); throw std::runtime_error(std::string("Error: Heap out of memory")); } @@ -92,6 +112,9 @@ namespace GC { if (profiler_enabled) Profiler::record(ReusedChunk, reused_chunk); + auto a_end = time_now; + auto a_ms = to_us(a_end - a_start); + Profiler::record(AllocStart, a_ms); return static_cast(reused_chunk->m_start); } @@ -100,11 +123,16 @@ namespace GC auto new_chunk = new Chunk(size, (uintptr_t *)(heap.m_heap + heap.m_size)); heap.m_size += size; + // TODO: handle this below + // heap.m_total_size += size; heap.m_allocated_chunks.push_back(new_chunk); if (profiler_enabled) Profiler::record(NewChunk, new_chunk); + auto a_end = time_now; + auto a_ms = to_us(a_end - a_start); + Profiler::record(AllocStart, a_ms); return new_chunk->m_start; } @@ -130,10 +158,11 @@ namespace GC // 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 = Heap::get_at(heap.m_freed_chunks, i); + // auto chunk = Heap::get_at(heap.m_freed_chunks, i); auto chunk = heap.m_freed_chunks[i]; auto iter = heap.m_freed_chunks.begin(); - advance(iter, i); + i++; + // advance(iter, i); if (chunk->m_size > size) { // Split the chunk, use one part and add the remaining part to @@ -159,33 +188,15 @@ namespace GC return nullptr; } - /** - * Advances an iterator and returns an element - * at position `n`. - * - * @param list The list to retrieve an element from. - * - * @param n The position to retrieve an element at. - * - * @returns The pointer to the chunk at position n in list. - */ - // Chunk *Heap::get_at(std::vector &list, size_t n) - // { - // auto iter = list.begin(); - // if (!n) - // return *iter; - // std::advance(iter, n); - // return *iter; - // } - /** * Returns a bool whether the profiler is enabled * or not. - * + * * @returns True or false if the profiler is enabled * or disabled respectively. - */ - bool Heap::profiler_enabled() { + */ + bool Heap::profiler_enabled() + { Heap &heap = Heap::the(); return heap.m_profiler_enable; } @@ -199,6 +210,8 @@ namespace GC */ void Heap::collect() { + auto c_start = time_now; + Heap &heap = Heap::the(); if (heap.profiler_enabled()) @@ -212,12 +225,20 @@ namespace GC uintptr_t *stack_top = heap.m_stack_top; - auto work_list = heap.m_allocated_chunks; - mark(stack_bottom, stack_top, work_list); + // auto work_list = heap.m_allocated_chunks; + // mark(stack_bottom, stack_top, work_list); + + // Testing mark_hash, previous woking implementation above + create_table(); + mark_hash(stack_bottom, stack_top); sweep(heap); free(heap); + + auto c_end = time_now; + + Profiler::record(CollectStart, to_us(c_end - c_start)); } /** @@ -227,8 +248,8 @@ namespace GC * 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. - * - * Time complexity: 0(N^2 * log(N)) as upper bound. + * + * Time complexity: 0(N^2 * log(N)) as upper bound. * Where N is either the size of the worklist or the size of * the stack frame, depending on which is the largest. * @@ -236,13 +257,16 @@ namespace GC * @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* const end, vector &worklist) + void Heap::mark(uintptr_t *start, const uintptr_t *const end, vector &worklist) { + // cout << "\nWorklist size: " << worklist.size() << "\n"; Heap &heap = Heap::the(); bool profiler_enabled = heap.m_profiler_enable; if (profiler_enabled) Profiler::record(MarkStart); + vector rangeWL; + // To find adresses thats in the worklist for (; start <= end; start++) { @@ -265,8 +289,22 @@ namespace GC chunk->m_marked = true; it = worklist.erase(it); + /* Chunk *next = find_pointer((uintptr_t *) c_start, (uintptr_t *) c_end, worklist); + while (next != NULL) { + if (!next->m_marked) + { + next->m_marked = true; + auto c_start = reinterpret_cast(next->m_start); + auto c_size = reinterpret_cast(next->m_size); + auto c_end = reinterpret_cast(c_start + c_size); + next = find_pointer((uintptr_t *) c_start, (uintptr_t *) c_end, worklist); + } + } */ + // Recursively call mark, to see if the reachable chunk further points to another chunk - mark((uintptr_t *)c_start, (uintptr_t *)c_end, worklist); + // mark((uintptr_t *)c_start, (uintptr_t *)c_end, worklist); + // AddrRange *range = new AddrRange((uintptr_t *)c_start, (uintptr_t *)c_end); + rangeWL.push_back(new AddrRange((uintptr_t *)c_start, (uintptr_t *)c_end)); } else { @@ -279,24 +317,136 @@ namespace GC } } } + mark_range(rangeWL, worklist); + rangeWL.clear(); } + void Heap::mark_range(vector &ranges, vector &worklist) + { + Heap &heap = Heap::the(); + bool profiler_enabled = heap.m_profiler_enable; + if (profiler_enabled) + Profiler::record(MarkStart); + + auto iter = ranges.begin(); + auto stop = ranges.end(); + + while (iter != stop) + { + auto range = *iter++; + uintptr_t *start = (uintptr_t *)range->start; + const uintptr_t *end = range->end; + if (start == nullptr) + cout << "\nstart is null\n"; + for (; start <= end; start++) + { + auto wliter = worklist.begin(); + auto wlstop = worklist.end(); + while (wliter != wlstop) + { + Chunk *chunk = *wliter; + auto c_start = reinterpret_cast(chunk->m_start); + auto c_size = reinterpret_cast(chunk->m_size); + auto c_end = reinterpret_cast(c_start + c_size); + + if (c_start <= *start && *start < c_end) + { + if (!chunk->m_marked) + { + chunk->m_marked = true; + wliter = worklist.erase(wliter); + ranges.push_back(new AddrRange((uintptr_t *)c_start, (uintptr_t *)c_end)); + stop = ranges.end(); + } + else + { + wliter++; + } + } + else + { + wliter++; + } + } + } + } + } + + void Heap::create_table() + { + Heap &heap = Heap::the(); + unordered_map chunk_table; + for (auto chunk : heap.m_allocated_chunks) + { + auto pair = std::make_pair(reinterpret_cast(chunk->m_start), chunk); + heap.m_chunk_table.insert(pair); + } + } + + void Heap::mark_hash(uintptr_t *start, const uintptr_t *const end) + { + Heap &heap = Heap::the(); + + bool profiler_enabled = heap.m_profiler_enable; + if (profiler_enabled) + Profiler::record(MarkStart); + + for (; start <= end; start++) + { + auto search = heap.m_chunk_table.find(*start); + if (search != heap.m_chunk_table.end()) + { + Chunk *chunk = search->second; + auto c_start = reinterpret_cast(chunk->m_start); + auto c_size = reinterpret_cast(chunk->m_size); + auto c_end = reinterpret_cast(c_start + c_size); + if (!chunk->m_marked) + { + chunk->m_marked = true; + + if (profiler_enabled) + Profiler::record(ChunkMarked, chunk); + + // mark_hash(chunk->m_start, c_end); + Chunk *next = find_pointer_hash((uintptr_t *)c_start, (uintptr_t *)c_end); + while (next != NULL) + { + if (!next->m_marked) + { + next->m_marked = true; + + if (profiler_enabled) + Profiler::record(ChunkMarked, chunk); + + auto c_start = reinterpret_cast(next->m_start); + auto c_size = reinterpret_cast(next->m_size); + auto c_end = reinterpret_cast(c_start + c_size); + next = find_pointer_hash((uintptr_t *)c_start, (uintptr_t *)c_end); + } + } + } + } + } + } /** * Sweeps the heap, unmarks the marked chunks for the next cycle, * adds the unmarked nodes to the list of freed chunks; to be freed. * - * Time complexity: O(N^2), where N is the number of allocated chunks. - * It is quadratic, in the worst case, - * since each call to erase() is linear. + * Time complexity: O(N^2), where N is the number of allocated chunks. + * It is quadratic, in the worst case, + * since each call to erase() is linear. * * @param heap Pointer to the heap singleton instance. */ void Heap::sweep(Heap &heap) { - auto iter = heap.m_allocated_chunks.begin(); bool profiler_enabled = heap.m_profiler_enable; - // This cannot "iter != stop", results in seg fault, since the end gets updated, I think. + if (profiler_enabled) + Profiler::record(SweepStart); + auto iter = heap.m_allocated_chunks.begin(); + // std::cout << "Chunks alloced: " << heap.m_allocated_chunks.size() << std::endl; + // This cannot "iter != stop", results in seg fault, since the end gets updated, I think. while (iter != heap.m_allocated_chunks.end()) { Chunk *chunk = *iter; @@ -315,8 +465,12 @@ namespace GC Profiler::record(ChunkSwept, chunk); heap.m_freed_chunks.push_back(chunk); iter = heap.m_allocated_chunks.erase(iter); + // heap.m_size -= chunk->m_size; + // cout << "Decremented total heap size with: " << chunk->m_size << endl; + // cout << "Total size is: " << heap.m_size << endl; } } + // std::cout << "Chunks left: " << heap.m_allocated_chunks.size() << std::endl; } /** @@ -324,7 +478,7 @@ namespace GC * by the sweep phase. If there are more than a certain * amount of free chunks, delete the free chunks to * avoid cluttering. - * + * * Time complexity: O(N^2), where N is the freed chunks. * If free_overlap() is called, it runs in O(N^2), * otherwise O(N). @@ -334,6 +488,9 @@ namespace GC */ void Heap::free(Heap &heap) { + bool profiler_enabled = heap.m_profiler_enable; + if (profiler_enabled) + Profiler::record(FreeStart); if (heap.m_freed_chunks.size() > FREE_THRESH) { bool profiler_enabled = heap.profiler_enabled(); @@ -343,6 +500,9 @@ namespace GC heap.m_freed_chunks.pop_back(); if (profiler_enabled) Profiler::record(ChunkFreed, chunk); + heap.m_size -= chunk->m_size; + // cout << "Decremented total heap size with: " << chunk->m_size << endl; + // cout << "Total size is: " << heap.m_size << endl; delete chunk; } } @@ -359,7 +519,7 @@ namespace GC * Checks for overlaps between freed chunks of memory * and removes overlapping chunks while prioritizing * the chunks at lower addresses. - * + * * Time complexity: O(N^2), where N is the number of freed chunks. * At each iteration get_at() is called, which is linear. * @@ -374,15 +534,15 @@ namespace GC { std::vector filtered; size_t i = 0; - //auto prev = Heap::get_at(heap.m_freed_chunks, i++); + // auto prev = Heap::get_at(heap.m_freed_chunks, i++); auto prev = heap.m_freed_chunks[i++]; prev->m_marked = true; filtered.push_back(prev); - cout << filtered.back()->m_start << endl; + // cout << filtered.back()->m_start << endl; for (; i < heap.m_freed_chunks.size(); i++) { prev = filtered.back(); - //auto next = Heap::get_at(heap.m_freed_chunks, i); + // auto next = Heap::get_at(heap.m_freed_chunks, i); auto next = heap.m_freed_chunks[i]; auto p_start = (uintptr_t)(prev->m_start); auto p_size = (uintptr_t)(prev->m_size); @@ -394,7 +554,7 @@ namespace GC } } heap.m_freed_chunks.swap(filtered); - + bool profiler_enabled = heap.m_profiler_enable; // After swap m_freed_chunks contains still available chunks // and filtered contains all the chunks, so delete unused chunks @@ -405,6 +565,9 @@ namespace GC { if (profiler_enabled) Profiler::record(ChunkFreed, chunk); + heap.m_size -= chunk->m_size; + // cout << "Decremented total heap size with: " << chunk->m_size << endl; + // cout << "Total size is: " << heap.m_size << endl; delete chunk; } else @@ -414,7 +577,51 @@ namespace GC } } -#ifdef DEBUG + void Heap::set_profiler(bool mode) + { + Heap &heap = Heap::the(); + heap.m_profiler_enable = mode; + } + + Chunk *find_pointer(uintptr_t *start, const uintptr_t *const end, vector &worklist) + { + for (; start <= end; start++) + { + auto it = worklist.begin(); + auto stop = worklist.end(); + while (it != stop) + { + Chunk *chunk = *it; + auto c_start = reinterpret_cast(chunk->m_start); + auto c_size = reinterpret_cast(chunk->m_size); + auto c_end = reinterpret_cast(c_start + c_size); + + // Check if the stack pointer points to something within the chunk + if (c_start <= *start && *start < c_end) + { + return chunk; + } + return NULL; + } + } + } + + // Checks if a given chunk points to another chunk and returns it + Chunk *Heap::find_pointer_hash(uintptr_t *start, const uintptr_t *const end) + { + Heap &heap = Heap::the(); + for (; start <= end; start++) + { + auto search = heap.m_chunk_table.find(*start); + if (search != heap.m_chunk_table.end()) + { + return search->second; + } + return NULL; + } + } + +#ifdef HEAP_DEBUG /** * Prints the result of Heap::init() and a dummy value * for the current stack frame for reference. @@ -530,7 +737,8 @@ namespace GC } else { - cout << "NO ALLOCATIONS\n" << endl; + cout << "NO ALLOCATIONS\n" + << endl; } if (heap.m_freed_chunks.size()) { @@ -553,7 +761,8 @@ namespace GC } else { - cout << "NO ALLOCATIONS\n" << endl; + cout << "NO ALLOCATIONS\n" + << endl; } if (heap.m_freed_chunks.size()) { @@ -565,15 +774,12 @@ namespace GC } } - void Heap::set_profiler(bool mode) + void Heap::print_allocated_chunks(Heap *heap) { - Heap &heap = Heap::the(); - heap.m_profiler_enable = mode; - } - - void Heap::print_allocated_chunks(Heap *heap) { - cout << "--- Allocated Chunks ---\n" << endl; - for (auto chunk : heap->m_allocated_chunks) { + cout << "--- Allocated Chunks ---\n" + << endl; + for (auto chunk : heap->m_allocated_chunks) + { print_line(chunk); } } @@ -584,9 +790,9 @@ namespace GC // 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 = heap.m_freed_chunks[i]; //Heap::get_at(heap.m_freed_chunks, i); + auto chunk = heap.m_freed_chunks[i]; // Heap::get_at(heap.m_freed_chunks, i); auto iter = heap.m_freed_chunks.begin(); - //advance(iter, i); + // advance(iter, i); i++; if (chunk->m_size > size) { @@ -617,14 +823,14 @@ namespace GC { std::vector filtered; size_t i = 0; - auto prev = heap.m_freed_chunks[i++]; //Heap::get_at(heap.m_freed_chunks, i++); + auto prev = heap.m_freed_chunks[i++]; // Heap::get_at(heap.m_freed_chunks, i++); prev->m_marked = true; filtered.push_back(prev); cout << filtered.back()->m_start << endl; for (; i < heap.m_freed_chunks.size(); i++) { prev = filtered.back(); - auto next = heap.m_freed_chunks[i]; //Heap::get_at(heap.m_freed_chunks, i); + auto next = heap.m_freed_chunks[i]; // Heap::get_at(heap.m_freed_chunks, i); auto p_start = (uintptr_t)(prev->m_start); auto p_size = (uintptr_t)(prev->m_size); auto n_start = (uintptr_t)(next->m_start); @@ -635,7 +841,7 @@ namespace GC } } heap.m_freed_chunks.swap(filtered); - + bool profiler_enabled = heap.m_profiler_enable; // After swap m_freed_chunks contains still available chunks // and filtered contains all the chunks, so delete unused chunks diff --git a/src/GC/lib/profiler.cpp b/src/GC/lib/profiler.cpp index 29abad4..78a0b8e 100644 --- a/src/GC/lib/profiler.cpp +++ b/src/GC/lib/profiler.cpp @@ -11,10 +11,42 @@ #include "event.hpp" #include "profiler.hpp" -// #define MAC_OS +#define MAC_OS namespace GC { + Profiler& Profiler::the() + { + static Profiler instance; + return instance; + } + + RecordOption Profiler::log_options() + { + Profiler &prof = Profiler::the(); + return prof.flags; + } + + void Profiler::set_log_options(RecordOption flags) + { + Profiler &prof = Profiler::the(); + prof.flags = flags; + } + + void Profiler::record_data(GCEvent *event) + { + Profiler &prof = Profiler::the(); + prof.m_events.push_back(event); + + if (prof.m_last_prof_event->m_type == event->get_type()) + prof.m_last_prof_event->m_n++; + else + { + prof.m_prof_events.push_back(prof.m_last_prof_event); + prof.m_last_prof_event = new ProfilerEvent(event->get_type()); + } + } + /** * Records an event independent of a chunk. * @@ -22,9 +54,12 @@ namespace GC */ void Profiler::record(GCEventType type) { - auto event = new GCEvent(type); - auto profiler = Profiler::the(); - profiler->m_events.push_back(event); + Profiler &prof = Profiler::the(); + if (prof.flags & type) + Profiler::record_data(new GCEvent(type)); + // auto event = new GCEvent(type); + // auto profiler = Profiler::the(); + // profiler.m_events.push_back(event); } /** @@ -37,9 +72,21 @@ namespace GC */ void Profiler::record(GCEventType type, size_t size) { - auto event = new GCEvent(type, size); - auto profiler = Profiler::the(); - profiler->m_events.push_back(event); + Profiler &prof = Profiler::the(); + if (prof.flags & type) + Profiler::record_data(new GCEvent(type, size)); + // auto event = new GCEvent(type, size); + // auto profiler = Profiler::the(); + // profiler.m_events.push_back(event); + } + + void Profiler::dump_trace() + { + Profiler &prof = Profiler::the(); + if (prof.flags & FunctionCalls) + dump_prof_trace(); + else + dump_chunk_trace(); } /** @@ -56,60 +103,114 @@ namespace GC // because in free() chunks are deleted and cannot // be referenced by the profiler. These copied // chunks are deleted by the profiler on dispose(). - auto chunk_copy = new Chunk(chunk); - auto event = new GCEvent(type, chunk_copy); - auto profiler = Profiler::the(); - profiler->m_events.push_back(event); + Profiler &prof = Profiler::the(); + if (prof.flags & type) + { + auto chunk_copy = new Chunk(chunk); + auto event = new GCEvent(type, chunk_copy); + Profiler::record_data(event); + } + // auto profiler = Profiler::the(); + // profiler.m_events.push_back(event); + } + + void Profiler::record(GCEventType type, std::chrono::microseconds time) + { + Profiler &prof = Profiler::the(); + if (type == AllocStart) + { + prof.alloc_time += time; + } + else if (type == CollectStart) + { + prof.collect_time += time; + } + } + + void Profiler::dump_prof_trace() + { + Profiler &prof = Profiler::the(); + prof.m_prof_events.push_back(prof.m_last_prof_event); + auto start = prof.m_prof_events.begin(); + auto end = prof.m_prof_events.end(); + int allocs = 0, collects = 0; + + char buffer[22]; + std::ofstream fstr = prof.create_file_stream(); + + while (start != end) + { + auto event = *start++; + + if (event->m_type == AllocStart) + allocs += event->m_n; + else if (event->m_type == CollectStart) + collects += event->m_n; + + fstr << "\n--------------------------------\n" + << Profiler::type_to_string(event->m_type) << " " + << event->m_n << " times:"; + } + fstr << "\n--------------------------------"; + + fstr << "\n\nTime spent on allocations:\t" << prof.alloc_time.count() << " microseconds" + << "\nAllocation cycles:\t" << allocs + << "\nTime spent on collections:\t" << prof.collect_time.count() << " microseconds" + << "\nCollection cycles:\t" << collects + << "\n--------------------------------"; } /** * Prints the history of the recorded events * to a log file in the /tests/logs folder. */ - void Profiler::dump_trace() + void Profiler::dump_chunk_trace() { - auto profiler = Profiler::the(); - auto start = profiler->m_events.begin(); - auto end = profiler->m_events.end(); + Profiler &prof = Profiler::the(); + auto start = prof.m_events.begin(); + auto end = prof.m_events.end(); - // File output stream - std::ofstream fstr = profiler->create_file_stream(); // Buffer for timestamp char buffer[22]; - // Time variables - std::tm *btm; - std::time_t tt; - const Chunk *chunk; while (start != end) { auto event = *start++; + auto e_type = event->get_type(); - tt = event->get_time_stamp(); - btm = std::localtime(&tt); - std::strftime(buffer, 22, "%a %T", btm); - - fstr << "--------------------------------\n" - << buffer - << "\nEvent:\t" << event->type_to_string(); - - - - chunk = event->get_chunk(); - - if (event->get_type() == AllocStart) - { - fstr << "\nSize: " << event->get_size(); - } - else if (chunk) - { - fstr << "\nChunk: " << chunk->m_start - << "\n Size: " << chunk->m_size - << "\n Mark: " << chunk->m_marked; - } - fstr << "\n"; + prof.print_chunk_event(event, buffer); } - fstr << "--------------------------------" << std::endl; + } + + void Profiler::print_chunk_event(GCEvent *event, char buffer[22]) + { + Profiler &prof = Profiler::the(); + // File output stream + std::ofstream fstr = prof.create_file_stream(); + std::time_t tt = event->get_time_stamp(); + std::tm *btm = std::localtime(&tt); + std::strftime(buffer, 22, "%a %T", btm); + + fstr << "--------------------------------\n" + << buffer + << "\nEvent:\t" << Profiler::type_to_string(event->get_type()); + // event->type_to_string(); + + + + const Chunk *chunk = event->get_chunk(); + + if (event->get_type() == AllocStart) + { + fstr << "\nSize: " << event->get_size(); + } + else if (chunk) + { + fstr << "\nChunk: " << chunk->m_start + << "\n Size: " << chunk->m_size + << "\n Mark: " << chunk->m_marked; + } + fstr << "\n"; } /** @@ -122,8 +223,6 @@ namespace GC { Profiler::record(ProfilerDispose); Profiler::dump_trace(); - auto profiler = Profiler::the(); - delete profiler; } /** @@ -189,4 +288,24 @@ namespace GC #endif return folder + "/logs"; } + + const char *Profiler::type_to_string(GCEventType type) + { + switch (type) + { + case HeapInit: return "HeapInit"; + case AllocStart: return "AllocStart"; + case CollectStart: return "CollectStart"; + case MarkStart: return "MarkStart"; + case ChunkMarked: return "ChunkMarked"; + case ChunkSwept: return "ChunkSwept"; + case ChunkFreed: return "ChunkFreed"; + case NewChunk: return "NewChunk"; + case ReusedChunk: return "ReusedChunk"; + case ProfilerDispose: return "ProfilerDispose"; + case SweepStart: return "SweepStart"; + case FreeStart: return "FreeStart"; + default: return "[Unknown]"; + } + } } \ No newline at end of file diff --git a/src/GC/tests/advance.cpp b/src/GC/tests/advance.cpp index 92ce506..89dca71 100644 --- a/src/GC/tests/advance.cpp +++ b/src/GC/tests/advance.cpp @@ -5,40 +5,79 @@ #include #include -int main() { - using namespace std; - using TimeStamp = std::chrono::_V2::system_clock::time_point; +// void time_test() +// { +// using TimeStamp = std::chrono::_V2::system_clock::time_point; - list l; - char c = 'a'; - for (int i = 1; i <= 5; i++) { - l.push_back(c++); - } +// std::list l; +// char c = 'a'; +// for (int i = 1; i <= 5; i++) { +// l.push_back(c++); +// } - auto iter = l.begin(); - auto stop = l.end(); +// auto iter = l.begin(); +// auto stop = l.end(); - while (iter != stop) { - cout << *iter << " "; +// while (iter != stop) { +// std::cout << *iter << " "; +// iter++; +// } +// std::cout << std::endl; +// iter = l.begin(); +// while (*iter != *stop) { +// std::cout << *iter << " "; +// iter++; +// } +// std::cout << std::endl; + +// std::cout << "rebased" << std::endl; +// std::cout << "iter: " << *iter << "\nstop: " << *stop << std::endl; + +// TimeStamp ts = std::chrono::system_clock::now(); +// std::time_t tt = std::chrono::system_clock::to_time_t(ts); +// std::string tstr = std::ctime(&tt); +// tstr.resize(tstr.size()-1); +// std::cout << tstr << std::endl; +// } + +void iter_test() +{ + std::list list; + list.push_back(1); + list.push_back(2); + list.push_back(4); + list.push_back(5); + + auto iter = list.begin(); + + while (iter != list.end()) + { + if (*iter == 4) + { + iter = list.erase(iter); + std::cout << *iter << "\n"; + list.insert(iter, 3); + // list.insert(iter, 3); + // std::cout << "n: " << *(++iter) << "\n"; + // iter = list.erase(++iter); + } iter++; } - cout << endl; - iter = l.begin(); - while (*iter != *stop) { - cout << *iter << " "; - iter++; + + for (int i : list) + { + std::cout << i << " "; } - cout << endl; + std::cout << std::endl; +} - cout << "rebased" << endl; - cout << "iter: " << *iter << "\nstop: " << *stop << endl; - TimeStamp ts = std::chrono::system_clock::now(); - std::time_t tt = std::chrono::system_clock::to_time_t(ts); - std::string tstr = std::ctime(&tt); - tstr.resize(tstr.size()-1); - std::cout << tstr << std::endl; + +int main() { + std::cout << "hello" << std::endl; + + iter_test(); return 0; } \ No newline at end of file diff --git a/src/GC/tests/alloc_free_list.cpp b/src/GC/tests/alloc_free_list.cpp new file mode 100644 index 0000000..a0d1a27 --- /dev/null +++ b/src/GC/tests/alloc_free_list.cpp @@ -0,0 +1,250 @@ +#include +#include + +#include "heap.hpp" + +using GC::Chunk; + +void alloc_test(); +void add_to_free_list(Chunk *chunk); +void merge_free_list(Chunk *chunk, bool do_merge); +void do_merge_list(); +void print_free_list(); + +std::list m_free_list; + +int main() +{ + alloc_test(); + + // std::list test; + + // test.push_back(1); + // test.push_back(2); + // test.push_back(3); + // test.push_back(4); + // test.push_back(5); + + // auto iter = test.begin(); + + // std::cout << "First? " << *(iter++) << "\n"; + // std::cout << "Second? " << *(iter--) << "\n"; + // std::cout << "First? " << *iter << std::endl; + + // auto i = test.begin(); + // while (i != test.end()) + // { + // std::cout << *i << " "; + // ++i; + // } + + // if (i == test.end()) + // std::cout << "great success!"; + + // std::cout << std::endl; + + return 0; +} + +void alloc_test() +{ + auto tmp = static_cast(__builtin_frame_address(0)); + + auto c1 = new Chunk((size_t)(8), tmp); + auto c2 = new Chunk((size_t)(4), c1->m_start + (size_t)(8)); + auto c3 = new Chunk((size_t)(16), c2->m_start + (size_t)(4)); + auto c4 = new Chunk((size_t)(4), c3->m_start + (size_t)(16)); + auto c5 = new Chunk((size_t)(32), c4->m_start + (size_t)(4)); + + // std::cout << "test: " << (uintptr_t *)(tmp + (size_t)(2)) << std::endl; + + std::cout << "tmp: " << tmp << "\ntmp: " << (tmp + (size_t)(28)) << std::endl; + + // add_to_free_list(c1); + // add_to_free_list(c2); + // add_to_free_list(c3); + // add_to_free_list(c4); + // add_to_free_list(c5); + + merge_free_list(c1, false); + merge_free_list(c2, false); + merge_free_list(c3, false); + merge_free_list(c4, false); + merge_free_list(c5, false); + + std::cout << "----- BEFORE MERGE ----------------------"; + // print_free_list(); + + do_merge_list(); + + std::cout << "----- AFTER MERGE -----------------------"; + // print_free_list(); +} + +void add_to_free_list(Chunk *chunk) +{ + Chunk *curr; + auto iter = m_free_list.begin(); + uintptr_t *prev_start = nullptr; + uintptr_t *prev_end = nullptr; + + if (m_free_list.size() == 0) + { + m_free_list.push_back(chunk); + return; + } + + while (iter != m_free_list.end()) + { + curr = *iter; + + // If the curr chunk is aligned before param + if (curr->m_start + curr->m_size == chunk->m_start) + { + Chunk *merged = new Chunk( + curr->m_size + chunk->m_size, + curr->m_start); + iter = m_free_list.erase(iter); + m_free_list.insert(iter, merged); + return; + } + + // If the curr chunk is aligned after param + if (chunk->m_start + chunk->m_size == curr->m_start) + { + Chunk *merged = new Chunk( + curr->m_size + chunk->m_size, + chunk->m_start); + iter = m_free_list.erase(iter); + m_free_list.insert(iter, merged); + return; + } + + // If the first chunk starts after param + if (prev_start == nullptr && curr->m_start > chunk->m_start) + { + m_free_list.insert(iter, chunk); + return; + } + + if (prev_end < chunk->m_start && (chunk->m_start + chunk->m_size) < curr->m_start) + { + m_free_list.insert(iter, chunk); + return; + } + + prev_start = curr->m_start; + prev_end = prev_start + curr->m_size; + iter++; + } + + // This is only reachable if the chunk is at the end + m_free_list.push_back(chunk); +} + +void merge_free_list(Chunk *chunk, bool do_merge) +{ + auto i = m_free_list.begin(); + uintptr_t *prev_start = nullptr, *prev_end; + bool chunk_inserted = false; + + while (i != m_free_list.end()) + { + + // if chunk is left-aligned + if ((*i)->m_start + (*i)->m_size == chunk->m_start) + { + m_free_list.insert(++i, chunk); + chunk_inserted = true; + break; + } + + // if chunk is right-aligned + if (chunk->m_start + chunk->m_size == (*i)->m_start) + { + m_free_list.insert(i, chunk); + chunk_inserted = true; + break; + } + + // is new first + if (prev_start == nullptr && (*i)->m_start > chunk->m_start) + { + m_free_list.insert(i, chunk); + chunk_inserted = true; + break; + } + + // if between chunks + if (prev_end < chunk->m_start && (chunk->m_start + chunk->m_size) < (*i)->m_start) + { + m_free_list.insert(i, chunk); + chunk_inserted = true; + break; + } + + prev_start = (*i)->m_start; + prev_end = (*i)->m_start + (*i)->m_size; + i++; + } + + // is new last + if (!chunk_inserted && i == m_free_list.end()) + m_free_list.push_back(chunk); + + if (do_merge) + do_merge_list(); +} + +void do_merge_list() +{ + std::cout << "DO MERGE" << std::endl; + auto i = m_free_list.begin(); + Chunk *prev = *(i++), *curr; + print_free_list(); + + while (i != m_free_list.end()) + { + curr = *i; + + if ((prev->m_start + prev->m_size) == curr->m_start) + { + Chunk *merged = new Chunk( + prev->m_size + curr->m_size, + prev->m_start + ); + + // replace current and previous with merged + i = m_free_list.erase(i); + i = m_free_list.erase(--i); + m_free_list.insert(i, merged); + + prev = merged; + } + else + { + prev = curr; + i++; + } + print_free_list(); + } + print_free_list(); +} + +void print_free_list() +{ + std::cout << "free-list count: " << m_free_list.size() << "\n"; + + auto iter = m_free_list.begin(); + size_t cnt = 1; + + while (iter != m_free_list.end()) + { + std::cout << "C" << cnt << ":\n\tstart: " << (*iter)->m_start + << "\n\tsize: " << (*iter)->m_size << "\n"; + iter++; + cnt++; + } + + std::cout << std::endl; +} \ No newline at end of file diff --git a/src/GC/tests/file.cpp b/src/GC/tests/file.cpp index df9e441..f4a0373 100644 --- a/src/GC/tests/file.cpp +++ b/src/GC/tests/file.cpp @@ -8,6 +8,7 @@ void time_string(char *buffer); void print_log_file(const std::string TESTS_PATH); void readlink_test(); +void null_test(); int main() { @@ -17,7 +18,9 @@ int main() // const std::string TESTS_PATH = "/home/virre/dev/systemF/org/language/src/GC/tests/"; // print_log_file(TESTS_PATH); - readlink_test(); + // readlink_test(); + + null_test(); return 0; } @@ -65,4 +68,10 @@ void readlink_test() std::string log_path = folder + "/log_file_bla.txt"; std::cout << "\n" << log_path << std::endl; +} + +void null_test() { + int *p = nullptr; + + std::cout << "P: " << nullptr << std::endl; } \ No newline at end of file diff --git a/src/GC/tests/h_test.cpp b/src/GC/tests/h_test.cpp index c871721..6ce727e 100644 --- a/src/GC/tests/h_test.cpp +++ b/src/GC/tests/h_test.cpp @@ -11,7 +11,7 @@ struct Node { }; Node *create_chain(int depth) { - cout << "entering create_chain"; + cout << "entering create_chain" << endl; std::vector nodes; if (depth > 0) { Node *last_node = static_cast(GC::Heap::alloc(sizeof(Node))); @@ -36,14 +36,14 @@ void create_array(size_t size) { } void detach_pointer(long **ptr) { - cout << "entering detach_pointer"; + cout << "entering detach_pointer" << endl; long *dummy_ptr = nullptr; *ptr = dummy_ptr; cout << "\nexiting detach_pointer" << endl; } Node *test_chain(int depth, bool detach) { - cout << "entering test_chain"; + cout << "entering test_chain" << endl; auto stack_start = reinterpret_cast(__builtin_frame_address(0)); Node *node_chain = create_chain(depth); @@ -80,14 +80,14 @@ int main() { GC::Heap::init(); GC::Heap &gc = GC::Heap::the(); gc.set_profiler(true); + GC::Profiler::set_log_options(GC::FunctionCalls); gc.check_init(); auto stack_start = reinterpret_cast(__builtin_frame_address(0)); Node *root1 = static_cast(gc.alloc(sizeof(Node))); Node *root2 = static_cast(gc.alloc(sizeof(Node))); - root1 = test_chain(58000, false); - root2 = test_chain(58000, false); - + root1 = test_chain(100000, false); + //root2 = test_chain(58000, false); gc.collect(GC::COLLECT_ALL); auto end = std::chrono::high_resolution_clock::now(); diff --git a/src/GC/tests/h_test.out.dSYM/Contents/Info.plist b/src/GC/tests/h_test.out.dSYM/Contents/Info.plist new file mode 100644 index 0000000..3db2218 --- /dev/null +++ b/src/GC/tests/h_test.out.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.h_test.out + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/src/GC/tests/linkedlist.cpp b/src/GC/tests/linkedlist.cpp new file mode 100644 index 0000000..b562a77 --- /dev/null +++ b/src/GC/tests/linkedlist.cpp @@ -0,0 +1,116 @@ +#include +#include + +#include "heap.hpp" + +#define allocNode static_cast(GC::Heap::alloc(sizeof(Node))) + +using std::cout, std::endl; + +struct Node // sizeof(Node) = 16 +{ + int value; + Node *next {nullptr}; +}; + +Node *create_list(size_t length) +{ + Node *head = allocNode; + head->value = 0; + + Node *prev = head; + Node *next; + + for (size_t i = 1; i < length; i++) + { + next = allocNode; + next->value = i; + prev->next = next; + prev = next; + } + + return head; +} + +void print_list(Node* head) +{ + cout << "\nPrinting list...\n"; + while (head != nullptr) + { + cout << head->value << " "; + head = head->next; + } + cout << endl; +} + +void clear_list(Node *head) +{ + while (head != nullptr) + { + Node *tmp = head->next; + head->next = nullptr; + head = tmp; + } +} + +#define LIST_SIZE 10 + +void list_test1() +{ + Node *list_1 = create_list(LIST_SIZE); + // print_list(list_1); +} + +void list_test2() +{ + Node *list_2 = create_list(LIST_SIZE); + // print_list(list_2); +} + +void list_test3() +{ + Node *list_3 = create_list(LIST_SIZE); + // print_list(list_3); +} + +void list_test4() +{ + Node *list_4 = create_list(LIST_SIZE); + // print_list(list_4); +} + +void list_test5() +{ + Node *list_5 = create_list(LIST_SIZE); + // print_list(list_5); +} + +void list_test6() +{ + Node *list_6 = create_list(LIST_SIZE); + // print_list(list_6); +} + +void make_test() { + list_test1(); + list_test2(); + list_test3(); + list_test4(); + list_test5(); + list_test6(); +} + +int main() +{ + GC::Heap::init(); + GC::Heap &heap = GC::Heap::the(); + heap.set_profiler(true); + GC::Profiler::set_log_options(GC::FunctionCalls); + //GC::Profiler::set_log_options(GC::AllOps); + + make_test(); + + GC::Heap::dispose(); + + return 0; +} \ No newline at end of file diff --git a/src/GC/tests/linker.cpp b/src/GC/tests/linker.cpp index 36717c5..fb5b979 100644 --- a/src/GC/tests/linker.cpp +++ b/src/GC/tests/linker.cpp @@ -9,22 +9,8 @@ struct Obj { }; int main() { - auto heap = GC::Heap::debug_the(); - - std::cout << "heap:\t" << heap << std::endl; - auto obj = static_cast(GC::Heap::alloc(sizeof(Obj))); - - std::cout << "obj: \t" << obj << std::endl; - - obj->a = 3; - obj->b = 4; - obj->c = 5; - - std::cout << obj->a << ", " << obj->b << ", " << obj->c << std::endl; - - heap->print_contents(); - //delete heap; + return 0; } \ No newline at end of file diff --git a/src/GC/tests/pointers.cpp b/src/GC/tests/pointers.cpp new file mode 100644 index 0000000..265ca30 --- /dev/null +++ b/src/GC/tests/pointers.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +#include "heap.hpp" + +using std::cout, std::endl, std::hex; + +struct Node { + int value; + Node *next {nullptr}; +}; + +void test(Node *n) { + size_t n_size = 16; + + auto c_start = reinterpret_cast(n); + auto c_size = reinterpret_cast(n_size); + auto c_end = reinterpret_cast(c_start + c_size); + + cout << "Node *n:\t" << n << "\n"; + cout << "n_size: \t0x" << std::hex << n_size << "\n"; + cout << "c_start:\t0x" << std::hex << c_start << "\n"; + cout << "c_size: \t0x" << std::hex << c_size << "\n"; + cout << "c_end: \t0x" << std::hex << c_end << endl; +} + +int main() { + GC::Heap::init(); + GC::Heap &heap = GC::Heap::the(); + heap.set_profiler(true); + heap.set_profiler_log_options(GC::FunctionCalls); + + Node *n = static_cast(GC::Heap::alloc(sizeof(Node))); + test(n); + + GC::Heap::dispose(); + return 0; +} \ No newline at end of file diff --git a/src/GC/tests/revrange.cpp b/src/GC/tests/revrange.cpp new file mode 100644 index 0000000..6a11c57 --- /dev/null +++ b/src/GC/tests/revrange.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "heap.hpp" + +#define allocNode static_cast(GC::Heap::alloc(sizeof(Node))) + +using std::cout, std::endl; + +struct Node { + int value; + Node *next {nullptr}; +}; + +void revRange(int n) { + Node *next = nullptr; + Node *prev = allocNode; + while (n > 0) { + next = allocNode; + prev->next = next; + prev->value = n--; + prev = next; + } +} + +void make_test() { + int n = 10; + while (n > 0) + revRange(1000); +} + +int main() { + GC::Heap::init(); + GC::Heap &heap = GC::Heap::the(); + heap.set_profiler(true); + GC::Profiler::set_log_options(GC::FunctionCalls); + + make_test(); + + GC::Heap::dispose(); + return 0; +} \ No newline at end of file diff --git a/src/GC/tests/wrapper.c b/src/GC/tests/wrapper.c new file mode 100644 index 0000000..d6f042c --- /dev/null +++ b/src/GC/tests/wrapper.c @@ -0,0 +1,96 @@ +#include +#include +#include + +#include "cheap.h" + +typedef struct object +{ + int x, y, z; + double velocity; +} Object; + +void test_init() +{ + printf("----- IN TEST_INIT ----------------------------\n"); + + cheap_init(); + + printf("----- EXIT TEST_INIT --------------------------\n"); +} + +/* Uncomment ONLY if run with DEBUG defined in cheap.h */ + +cheap_t *test_the() +{ + printf("----- IN TEST_THE -----------------------------\n"); + + cheap_t *fst_heap = cheap_the(); + + printf("Heap 1:\t%p\n", fst_heap->obj); + + cheap_t *snd_heap = cheap_the(); + + printf("Heap 2:\t%p\n", snd_heap->obj); + + printf("----- EXIT TEST_THE ---------------------------\n"); + + free(snd_heap); + return fst_heap; +} + +void test_profiler(cheap_t *heap) +{ + printf("----- IN TEST_PROFILER ------------------------\n"); + + cheap_set_profiler(heap, false); + cheap_set_profiler(heap, true); + cheap_profiler_log_options(heap, FuncCallsOnly); + + printf("----- EXIT TEST_PROFILER ----------------------\n"); +} + +Object *test_alloc() +{ + printf("----- IN TEST_ALLOC ---------------------------\n"); + + Object *o; + o = (Object *)(cheap_alloc(sizeof(Object))); + + o->x = 3; + o->y = 4; + o->z = 5; + o->velocity = 1.0f; + + printf("----- EXIT TEST_ALLOC -------------------------\n"); + return o; +} + +void test_dispose() +{ + printf("----- IN TEST_DISPOSE -------------------------\n"); + + cheap_dispose(); + + printf("----- EXIT TEST_DISPOSE -----------------------\n"); +} + +int main() +{ + test_init(); + + /* Uncomment ONLY if run with DEBUG defined in cheap.h */ + cheap_t *heap = test_the(); + test_profiler(heap); + + Object *o = test_alloc(); + printf("Object size: %lu\n", sizeof(Object)); + printf("Object:\n\tx: %d\n\ty: %d\n\tz: %d\n\tvel: %f\n", o->x, o->y, o->z, o->velocity); + + test_dispose(); + + /* Sefault I don't understand, don't uncomment */ + // free(heap); + // free(o); + return 0; +} \ No newline at end of file diff --git a/src/GC/tests/wrapper_test.c b/src/GC/tests/wrapper_test.c new file mode 100644 index 0000000..2055b8c --- /dev/null +++ b/src/GC/tests/wrapper_test.c @@ -0,0 +1,53 @@ +#include +#include + +#include "cheap.h" + +typedef struct node { + int id; + struct node *child; +} Node; + +// Global variables make the test less complex +Node *HEAD = NULL; +Node *CURRENT = NULL; + +void insert_first(int node_id) { + Node *new_head; + new_head = (Node*)(cheap_alloc(sizeof(Node))); + new_head->id = node_id; + new_head->child = HEAD; + + HEAD = new_head; +} + +// Creates a linked list of length depth. Global head "HEAD" is updated. +Node *create_linked_list(int depth) { + HEAD = (Node*)(cheap_alloc(sizeof(Node))); + HEAD->id = 0; + // Purposely omitting adding a child to "last_node", since its the last node + for (int i = 1; i < depth - 1; i++) { + insert_first(i); + } + return HEAD; +} + +void create_garbage(int amount) { + for (int i = 0; i < amount; i++) { + long *garbage = (long*)(cheap_alloc(sizeof(long))); + } +} + +int main () { + cheap_init(); + cheap_t *heap = cheap_the(); + cheap_set_profiler(heap, true); + + // Every node in this list should be marked + Node *head = create_linked_list(5); + // Everything create here should be swept + create_garbage(30); + + cheap_dispose(); + return 0; +} \ No newline at end of file diff --git a/src/Main.hs b/src/Main.hs index b487222..34512e6 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -138,8 +138,8 @@ main' opts s = log monomorphized printToErr "\n -- Compiler --" - -- generatedCode <- fromErr $ generateCode monomorphized (gc opts) - generatedCode <- fromErr $ generateCode monomorphized False + generatedCode <- fromErr $ generateCode monomorphized (gc opts) + -- generatedCode <- fromErr $ generateCode monomorphized False check <- doesPathExist "output" when check (removeDirectoryRecursive "output") @@ -149,8 +149,8 @@ main' opts s = writeFile "output/llvm.ll" generatedCode debugDotViz - -- compile generatedCode (gc opts) - compile generatedCode False + compile generatedCode (gc opts) + -- compile generatedCode False printToErr "Compilation done!" printToErr "\n-- Program output --" print =<< spawnWait "./output/hello_world"