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

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

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

View file

@ -31,9 +31,9 @@ namespace GC {
void collect();
void 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<Chunk *> worklist);
void print_line(Chunk *chunk);
void print_worklist(std::vector<Chunk *> 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<Chunk *> m_allocated_chunks;
std::vector<Chunk *> 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

View file

@ -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,
/**
* Initialises the heap singleton and saves the address
* of the calling stack frame as the stack_end. Presumeably
* this address points to the stack frame of the compiled
* LLVM executable after linking. (NOT CONFIRMED)
*/
void Heap::init() {
Heap *heap = Heap::the();
heap->m_stack_end = reinterpret_cast<uintptr_t *>(__builtin_frame_address(1));
}
/**
* Allocates a given amount of bytes on the heap.
*
* @param size The amount of bytes to be allocated.
*
* @return A pointer to the address where the memory
* has been allocated. This pointer is supposed
* to be casted to and object pointer.
*/
void *Heap::alloc(size_t size) {
auto heap = Heap::the();
assert(size > 0 && "Heap: Cannot alloc less than 0B");
if (heap.m_size + size > HEAP_SIZE) {
// try collect
// Singleton
Heap *heap = Heap::the();
if (size < 0) {
cout << "Heap: Cannot alloc less than 0B. No bytes allocated." << endl;
return nullptr;
}
if (heap->m_size + size > HEAP_SIZE) {
collect();
assert(heap.m_size + size <= HEAP_SIZE && "Heap: Out Of Memory");
// If collect failed, crash with OOM error
assert(heap->m_size + size <= HEAP_SIZE && "Heap: Out Of Memory");
}
// kolla freed chunks innan
// 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;
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;
}
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<uintptr_t *>(__builtin_frame_address(0));
// looking at 10 stack frames back
const uintptr_t *stack_end = (uintptr_t *)0; // temporary
if (heap->m_stack_end != nullptr)
stack_end = heap->m_stack_end;
else
stack_end = (uintptr_t *)0; // temporary
// denna segfaultar om arg för __b_f_a är > 2
// reinterpret_cast<const uintptr_t *>(__builtin_frame_address(10));
auto work_list = m_allocated_chunks;
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<Chunk *> worklist) {
while (worklist.size() > 0) {
Chunk *ref = worklist.pop_back();
Chunk *child = (Chunk*) *ref;
if (child != NULL && !child->marked) {
child->marked = true;
worklist.push_back(child);
mark_test(worklist);
}
}
}
void mark_from_roots(uintptr_t *start, const uintptr_t *end) {
vector<Chunk *> worklist;
for (;start > end; start--) {
Chunk *ref = *start;
if (ref != NULL && !ref->marked) {
ref->marked = true;
worklist.push_back(ref);
mark_test(worklist);
}
}
} */
/* Alternative marking, pseudocode
mark_from_roots():
worklist <- empty
for fld in Roots
ref <- *fld
if ref null && !marked(ref)
set_marked(ref)
worklist.add(ref)
mark()
mark():
while size(worklist) > 0
ref <- remove_first(worklist)
for fld in Pointers(ref)
child <- *fld
if child null && !marked(child)
set_marked(child)
worklist.add(child)
*/
// For testing purposes
void Heap::print_line(Chunk *chunk) {
cout << "Marked: " << chunk->marked << "\nStart adr: " << chunk->start << "\nSize: " << chunk->size << " B\n" << endl;

View file

@ -7,7 +7,7 @@ Goal for next week (24/2):
## GC TODO:
- 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