Author: Nicolas Truessel <[email protected]>
Branch: quad-color-gc
Changeset: r86305:45d8ca00efff
Date: 2016-08-19 09:41 +0200
http://bitbucket.org/pypy/pypy/changeset/45d8ca00efff/

Log:    Import qcgc codebase

diff --git a/rpython/translator/c/src/qcgc/allocator.c 
b/rpython/translator/c/src/qcgc/allocator.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/allocator.c
@@ -0,0 +1,293 @@
+#include "allocator.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+QCGC_STATIC size_t bytes_to_cells(size_t bytes);
+
+QCGC_STATIC void bump_allocator_assign(cell_t *ptr, size_t cells);
+QCGC_STATIC cell_t *bump_allocator_allocate(size_t cells);
+QCGC_STATIC void bump_allocator_advance(size_t cells);
+
+QCGC_STATIC bool is_small(size_t cells);
+QCGC_STATIC size_t small_index(size_t cells);
+QCGC_STATIC size_t large_index(size_t cells);
+QCGC_STATIC size_t small_index_to_cells(size_t index);
+
+QCGC_STATIC cell_t *fit_allocator_allocate(size_t cells);
+QCGC_STATIC cell_t *fit_allocator_small_first_fit(size_t index, size_t cells);
+QCGC_STATIC cell_t *fit_allocator_large_fit(size_t index, size_t cells);
+QCGC_STATIC cell_t *fit_allocator_large_first_fit(size_t index, size_t cells);
+
+QCGC_STATIC bool valid_block(cell_t *ptr, size_t cells);
+
+void qcgc_allocator_initialize(void) {
+       qcgc_allocator_state.arenas =
+               qcgc_arena_bag_create(QCGC_ARENA_BAG_INIT_SIZE);
+
+       // Bump Allocator
+       qcgc_allocator_state.bump_state.bump_ptr = NULL;
+       qcgc_allocator_state.bump_state.remaining_cells = 0;
+
+       // Fit Allocator
+       for (size_t i = 0; i < QCGC_SMALL_FREE_LISTS; i++) {
+               qcgc_allocator_state.fit_state.small_free_list[i] =
+                       
qcgc_linear_free_list_create(QCGC_SMALL_FREE_LIST_INIT_SIZE);
+       }
+
+       for (size_t i = 0; i < QCGC_LARGE_FREE_LISTS; i++) {
+               qcgc_allocator_state.fit_state.large_free_list[i] =
+                       
qcgc_exp_free_list_create(QCGC_LARGE_FREE_LIST_INIT_SIZE);
+       }
+}
+
+void qcgc_allocator_destroy(void) {
+       // Fit Allocator
+       for (size_t i = 0; i < QCGC_SMALL_FREE_LISTS; i++) {
+               free(qcgc_allocator_state.fit_state.small_free_list[i]);
+       }
+
+       for (size_t i = 0; i < QCGC_LARGE_FREE_LISTS; i++) {
+               free(qcgc_allocator_state.fit_state.large_free_list[i]);
+       }
+
+       // Arenas
+       size_t arena_count = qcgc_allocator_state.arenas->count;
+       for (size_t i = 0; i < arena_count; i++) {
+               qcgc_arena_destroy(qcgc_allocator_state.arenas->items[i]);
+       }
+
+       free(qcgc_allocator_state.arenas);
+}
+
+cell_t *qcgc_allocator_allocate(size_t bytes) {
+       size_t size_in_cells = bytes_to_cells(bytes);
+#if CHECKED
+       assert(size_in_cells > 0);
+       assert(size_in_cells <= QCGC_ARENA_CELLS_COUNT - 
QCGC_ARENA_FIRST_CELL_INDEX);
+#endif
+       cell_t *result;
+
+       // TODO: Implement switch for bump/fit allocator
+       if (true) {
+               result = bump_allocator_allocate(size_in_cells);
+       } else {
+               result = fit_allocator_allocate(size_in_cells);
+       }
+
+       qcgc_arena_mark_allocated(result, size_in_cells);
+#if QCGC_INIT_ZERO
+       memset(result, 0, bytes);
+#endif
+       return result;
+}
+
+void qcgc_fit_allocator_add(cell_t *ptr, size_t cells) {
+       if (cells > 0) {
+               if (is_small(cells)) {
+                       size_t index = small_index(cells);
+                       qcgc_allocator_state.fit_state.small_free_list[index] =
+                               qcgc_linear_free_list_add(
+                                               
qcgc_allocator_state.fit_state.small_free_list[index],
+                                               ptr);
+               } else {
+                       size_t index = large_index(cells);
+                       qcgc_allocator_state.fit_state.large_free_list[index] =
+                               qcgc_exp_free_list_add(
+                                               
qcgc_allocator_state.fit_state.large_free_list[index],
+                                               (struct exp_free_list_item_s) 
{ptr, cells});
+               }
+       }
+}
+
+QCGC_STATIC void bump_allocator_assign(cell_t *ptr, size_t cells) {
+       qcgc_allocator_state.bump_state.bump_ptr = ptr;
+       qcgc_allocator_state.bump_state.remaining_cells = cells;
+}
+
+QCGC_STATIC void bump_allocator_advance(size_t cells) {
+       qcgc_allocator_state.bump_state.bump_ptr += cells;
+       qcgc_allocator_state.bump_state.remaining_cells -= cells;
+}
+
+QCGC_STATIC cell_t *bump_allocator_allocate(size_t cells) {
+       if (cells > qcgc_allocator_state.bump_state.remaining_cells) {
+               // Grab a new arena
+               arena_t *arena = qcgc_arena_create();
+               
bump_allocator_assign(&(arena->cells[QCGC_ARENA_FIRST_CELL_INDEX]),
+                               QCGC_ARENA_CELLS_COUNT - 
QCGC_ARENA_FIRST_CELL_INDEX);
+               qcgc_allocator_state.arenas =
+                       qcgc_arena_bag_add(qcgc_allocator_state.arenas, arena);
+       }
+       cell_t *result = qcgc_allocator_state.bump_state.bump_ptr;
+       bump_allocator_advance(cells);
+       return result;
+}
+
+QCGC_STATIC cell_t *fit_allocator_allocate(size_t cells) {
+       cell_t *result;
+
+       if (is_small(cells)) {
+               size_t index = small_index(cells);
+               result = fit_allocator_small_first_fit(index, cells);
+       } else {
+               size_t index = large_index(cells);
+               result = fit_allocator_large_fit(index, cells);
+       }
+
+       if (result == NULL) {
+               // No valid block found
+               result = bump_allocator_allocate(cells);
+       }
+
+       return result;
+}
+
+QCGC_STATIC cell_t *fit_allocator_small_first_fit(size_t index, size_t cells) {
+#if CHECKED
+       assert(small_index_to_cells(index) >= cells);
+#endif
+       cell_t *result = NULL;
+       for ( ; index < QCGC_SMALL_FREE_LISTS; index++) {
+               linear_free_list_t *free_list =
+                       qcgc_allocator_state.fit_state.small_free_list[index];
+               size_t list_cell_size = small_index_to_cells(index);
+
+               while (free_list->count > 0) {
+                       result = free_list->items[free_list->count - 1];
+                       free_list = 
qcgc_linear_free_list_remove_index(free_list,
+                                       free_list->count - 1);
+
+                       // Check whether block is still valid
+                       if (valid_block(result, list_cell_size)) {
+                               qcgc_fit_allocator_add(result + cells, 
list_cell_size - cells);
+                               break;
+                       } else {
+                               result = NULL;
+                       }
+               }
+
+               qcgc_allocator_state.fit_state.small_free_list[index] = 
free_list;
+               if (result != NULL) {
+                       return result;
+               }
+       }
+       return fit_allocator_large_first_fit(0, cells);
+}
+
+QCGC_STATIC cell_t *fit_allocator_large_fit(size_t index, size_t cells) {
+#if CHECKED
+       assert(1u<<(index + QCGC_LARGE_FREE_LIST_FIRST_EXP) <= cells);
+       assert(1u<<(index + QCGC_LARGE_FREE_LIST_FIRST_EXP + 1) > cells);
+#endif
+       exp_free_list_t *free_list =
+               qcgc_allocator_state.fit_state.large_free_list[index];
+       size_t best_fit_index = free_list->count;
+
+       cell_t *result = NULL;
+       size_t best_fit_cells = SIZE_MAX;
+       size_t i = 0;
+       while (i < free_list->count) {
+               if (valid_block(free_list->items[i].ptr, 
free_list->items[i].size)) {
+                       if (free_list->items[i].size >= cells &&
+                                       free_list->items[i].size < 
best_fit_cells) {
+                               result = free_list->items[i].ptr;
+                               best_fit_cells = free_list->items[i].size;
+                               best_fit_index = i;
+                       }
+                       i++;
+               } else {
+                       free_list = qcgc_exp_free_list_remove_index(free_list, 
i);
+                       // NO i++ !
+               }
+
+               if (best_fit_cells == cells) {
+                       break;
+               }
+       }
+
+       if (result != NULL) {
+               // Best fit was found
+               assert(best_fit_index < free_list->count);
+               free_list = qcgc_exp_free_list_remove_index(free_list, 
best_fit_index);
+               qcgc_fit_allocator_add(result + cells, best_fit_cells - cells);
+       } else {
+               // No best fit, go for first fit
+               result = fit_allocator_large_first_fit(index + 1, cells);
+       }
+       qcgc_allocator_state.fit_state.large_free_list[index] = free_list;
+       return result;
+}
+
+QCGC_STATIC cell_t *fit_allocator_large_first_fit(size_t index, size_t cells) {
+#if CHECKED
+       assert(1u<<(index + QCGC_LARGE_FREE_LIST_FIRST_EXP) >= cells);
+#endif
+       cell_t *result = NULL;
+       for ( ; index < QCGC_LARGE_FREE_LISTS; index++) {
+               exp_free_list_t *free_list =
+                       qcgc_allocator_state.fit_state.large_free_list[index];
+               while(free_list->count > 0) {
+                       struct exp_free_list_item_s item =
+                               free_list->items[free_list->count - 1];
+                       free_list = qcgc_exp_free_list_remove_index(free_list,
+                                       free_list->count - 1);
+
+                       // Check whether block is still valid
+                       if (valid_block(item.ptr, item.size)) {
+                               qcgc_fit_allocator_add(item.ptr + cells, 
item.size - cells);
+                               result = item.ptr;
+                               break;
+                       }
+               }
+               qcgc_allocator_state.fit_state.large_free_list[index] = 
free_list;
+               if (result != NULL) {
+                       return result;
+               }
+       }
+       return NULL;
+}
+
+QCGC_STATIC size_t bytes_to_cells(size_t bytes) {
+       return (bytes + sizeof(cell_t) - 1) / sizeof(cell_t);
+}
+
+QCGC_STATIC bool is_small(size_t cells) {
+       return cells <= QCGC_SMALL_FREE_LISTS;
+}
+
+QCGC_STATIC size_t small_index(size_t cells) {
+#if CHECKED
+       assert(is_small(cells));
+#endif
+       return cells - 1;
+}
+
+QCGC_STATIC size_t large_index(size_t cells) {
+#if CHECKED
+       assert(!is_small(cells));
+#endif
+       // shift such that the meaningless part disappears, i.e. everything that
+       // belongs into the first free list will become 1.
+       cells = cells >> QCGC_LARGE_FREE_LIST_FIRST_EXP;
+
+       // calculates floor(log(cells))
+       return (8 * sizeof(unsigned long)) - __builtin_clzl(cells) - 1;
+}
+
+QCGC_STATIC size_t small_index_to_cells(size_t index) {
+#if CHECKED
+       assert(index < QCGC_SMALL_FREE_LISTS);
+#endif
+       return index + 1;
+}
+
+QCGC_STATIC bool valid_block(cell_t *ptr, size_t cells) {
+#if CHECKED
+       assert(ptr != NULL);
+       assert(cells > 0);
+#endif
+       return (qcgc_arena_get_blocktype(ptr) == BLOCK_FREE &&
+                       qcgc_arena_get_blocktype(ptr + cells) != BLOCK_EXTENT);
+}
diff --git a/rpython/translator/c/src/qcgc/allocator.h 
b/rpython/translator/c/src/qcgc/allocator.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/allocator.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "config.h"
+
+#include <stddef.h>
+
+#include "arena.h"
+#include "bag.h"
+#include "object.h"
+
+/**
+ * Free lists:
+ *
+ * Small free lists:
+ *                +---+---+-----+----+
+ * index:         | 0 | 1 | ... | 30 |
+ *                +---+---+-----+----+
+ * size (cells):  | 1 | 2 | ... | 31 |
+ *                +---+---+-----+----+
+ *
+ * Large free lists:
+ *                        +-----+-----+-----+---------+
+ * index:                 |  0  |  1  | ... |    x    |
+ *                        +-----+-----+-----+---------+
+ * minimal size (cells):  | 2^5 | 2^6 | ... | 2^(x+5) |
+ *                        +-----+-----+-----+---------+
+ *
+ * where x is chosen such that x + 5 + 1 = QCGC_ARENA_SIZE_EXP - 4 (i.e. the
+ * next bin would hold chunks that have the size of at least one arena size,
+ * which is impossible as an arena contains overhead)
+ */
+
+#define QCGC_LARGE_FREE_LISTS (QCGC_ARENA_SIZE_EXP - 4 - 
QCGC_LARGE_FREE_LIST_FIRST_EXP)
+
+#define QCGC_SMALL_FREE_LISTS ((1<<QCGC_LARGE_FREE_LIST_FIRST_EXP) - 1)
+
+struct qcgc_allocator_state {
+       arena_bag_t *arenas;
+       struct bump_state {
+               cell_t *bump_ptr;
+               size_t remaining_cells;
+       } bump_state;
+       struct fit_state {
+               linear_free_list_t *small_free_list[QCGC_SMALL_FREE_LISTS];
+               exp_free_list_t *large_free_list[QCGC_LARGE_FREE_LISTS];
+       } fit_state;
+} qcgc_allocator_state;
+
+/**
+ * Initialize allocator
+ */
+void qcgc_allocator_initialize(void);
+
+/**
+ * Destroy allocator
+ */
+void qcgc_allocator_destroy(void);
+
+/**
+ * Allocate new memory region
+ *
+ * @param      bytes   Desired size of the memory region in bytes
+ * @return     Pointer to memory large enough to hold size bytes, NULL in case 
of
+ *                     errors, already zero initialized if QCGC_INIT_ZERO is 
set
+ */
+cell_t *qcgc_allocator_allocate(size_t bytes);
+
+
+/**
+ * Add memory to free lists
+ *
+ * @param      ptr             Pointer to memory region
+ * @param      cells   Size of memory region in cells
+ */
+void qcgc_fit_allocator_add(cell_t *ptr, size_t cells);
diff --git a/rpython/translator/c/src/qcgc/arena.c 
b/rpython/translator/c/src/qcgc/arena.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/arena.c
@@ -0,0 +1,322 @@
+#include "arena.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "allocator.h"
+#include "event_logger.h"
+
+/**
+ * Internal functions
+ */
+QCGC_STATIC blocktype_t get_blocktype(arena_t *arena, size_t index);
+QCGC_STATIC void set_blocktype(arena_t *arena, size_t index, blocktype_t type);
+
+arena_t *qcgc_arena_create(void) {
+       qcgc_event_logger_log(EVENT_NEW_ARENA, 0, NULL);
+
+       arena_t *result;
+       // Linux: MAP_ANONYMOUS is initialized to zero
+       cell_t *mem = (cell_t *) mmap(0, 2 * QCGC_ARENA_SIZE,
+                       PROT_READ | PROT_WRITE,
+                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+       if (mem == MAP_FAILED) {
+               // ERROR: OUT OF MEMORY
+               return NULL;
+       }
+       if (mem != qcgc_arena_addr(mem)->cells) {
+               // Not aligned -> align
+               cell_t *aligned_mem = (cell_t *)(
+                               (intptr_t) qcgc_arena_addr(mem) + 
QCGC_ARENA_SIZE);
+               size_t size_before = (size_t)((intptr_t) aligned_mem - 
(intptr_t) mem);
+               size_t size_after = QCGC_ARENA_SIZE - size_before;
+
+               munmap((void *) mem, size_before);
+               munmap((void *)((intptr_t) aligned_mem + QCGC_ARENA_SIZE), 
size_after);
+               result = (arena_t *) aligned_mem;
+       } else {
+               // free second half
+               munmap((void *)((intptr_t) mem + QCGC_ARENA_SIZE), 
QCGC_ARENA_SIZE);
+               result = (arena_t *) mem;
+       }
+
+       // Init bitmaps: One large free block
+       qcgc_arena_set_bitmap_entry(result->mark_bitmap, 
QCGC_ARENA_FIRST_CELL_INDEX, true);
+
+       // Create gray stack
+       result->gray_stack = qcgc_gray_stack_create(QCGC_GRAY_STACK_INIT_SIZE);
+       return result;
+}
+
+void qcgc_arena_destroy(arena_t *arena) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       free(arena->gray_stack);
+       munmap((void *) arena, QCGC_ARENA_SIZE);
+}
+
+arena_t *qcgc_arena_addr(cell_t *ptr) {
+       return (arena_t *)((intptr_t) ptr & ~(QCGC_ARENA_SIZE - 1));
+}
+
+size_t qcgc_arena_cell_index(cell_t *ptr) {
+       return (size_t)((intptr_t) ptr & (QCGC_ARENA_SIZE - 1)) >> 4;
+}
+
+bool qcgc_arena_get_bitmap_entry(uint8_t *bitmap, size_t index) {
+#if CHECKED
+       assert(bitmap != NULL);
+#endif
+       return (((bitmap[index / 8] >> (index % 8)) & 0x1) == 0x01);
+}
+
+void qcgc_arena_set_bitmap_entry(uint8_t *bitmap, size_t index, bool value) {
+#if CHECKED
+       assert(bitmap != NULL);
+#endif
+       if (value) {
+               bitmap[index / 8] |= 1<<(index % 8);
+       } else {
+               bitmap[index / 8] &= ~(1<<(index % 8));
+       }
+}
+
+QCGC_STATIC blocktype_t get_blocktype(arena_t *arena, size_t index) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       uint8_t block_bit = qcgc_arena_get_bitmap_entry(arena->block_bitmap, 
index);
+       uint8_t mark_bit = qcgc_arena_get_bitmap_entry(arena->mark_bitmap, 
index);
+
+       if (block_bit) {
+               if (mark_bit) {
+                       return BLOCK_BLACK;
+               } else {
+                       return BLOCK_WHITE;
+               }
+       } else {
+               if (mark_bit) {
+                       return BLOCK_FREE;
+               } else {
+                       return BLOCK_EXTENT;
+               }
+       }
+}
+
+blocktype_t qcgc_arena_get_blocktype(cell_t *ptr) {
+       size_t index = qcgc_arena_cell_index(ptr);
+       arena_t *arena = qcgc_arena_addr(ptr);
+
+       return get_blocktype(arena, index);
+}
+
+QCGC_STATIC void set_blocktype(arena_t *arena, size_t index, blocktype_t type) 
{
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       switch(type) {
+               case BLOCK_EXTENT:
+                       qcgc_arena_set_bitmap_entry(arena->block_bitmap, index, 
false);
+                       qcgc_arena_set_bitmap_entry(arena->mark_bitmap, index, 
false);
+                       break;
+               case BLOCK_FREE:
+                       qcgc_arena_set_bitmap_entry(arena->block_bitmap, index, 
false);
+                       qcgc_arena_set_bitmap_entry(arena->mark_bitmap, index, 
true);
+                       break;
+               case BLOCK_WHITE:
+                       qcgc_arena_set_bitmap_entry(arena->block_bitmap, index, 
true);
+                       qcgc_arena_set_bitmap_entry(arena->mark_bitmap, index, 
false);
+                       break;
+               case BLOCK_BLACK:
+                       qcgc_arena_set_bitmap_entry(arena->mark_bitmap, index, 
true);
+                       qcgc_arena_set_bitmap_entry(arena->block_bitmap, index, 
true);
+                       break;
+       }
+}
+
+void qcgc_arena_set_blocktype(cell_t *ptr, blocktype_t type) {
+       size_t index = qcgc_arena_cell_index(ptr);
+       arena_t *arena = qcgc_arena_addr(ptr);
+       set_blocktype(arena, index, type);
+}
+
+void qcgc_arena_mark_allocated(cell_t *ptr, size_t cells) {
+       size_t index = qcgc_arena_cell_index(ptr);
+       arena_t *arena = qcgc_arena_addr(ptr);
+       set_blocktype(arena, index, BLOCK_WHITE);
+       size_t index_of_next_block = index + cells;
+       if (index_of_next_block < QCGC_ARENA_CELLS_COUNT &&
+                       get_blocktype(arena, index_of_next_block) == 
BLOCK_EXTENT) {
+               set_blocktype(arena, index_of_next_block, BLOCK_FREE);
+       }
+}
+
+void qcgc_arena_mark_free(cell_t *ptr) {
+       qcgc_arena_set_blocktype(ptr, BLOCK_FREE);
+       // No coalescing, collector will do this
+}
+
+bool qcgc_arena_sweep(arena_t *arena) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       bool free = true;
+       bool coalesce = false;
+       size_t last_free_cell = QCGC_ARENA_FIRST_CELL_INDEX;
+       for (size_t cell = QCGC_ARENA_FIRST_CELL_INDEX;
+                       cell < QCGC_ARENA_CELLS_COUNT;
+                       cell++) {
+               switch (qcgc_arena_get_blocktype(arena->cells + cell)) {
+                       case BLOCK_EXTENT:
+                               break;
+                       case BLOCK_FREE:
+                               if (coalesce) {
+                                       set_blocktype(arena, cell, 
BLOCK_EXTENT);
+                               } else {
+                                       last_free_cell = cell;
+                               }
+                               coalesce = true;
+                               break;
+                       case BLOCK_WHITE:
+                               if (coalesce) {
+                                       set_blocktype(arena, cell, 
BLOCK_EXTENT);
+                               } else {
+                                       set_blocktype(arena, cell, BLOCK_FREE);
+                                       last_free_cell = cell;
+                               }
+                               coalesce = true;
+                               break;
+                       case BLOCK_BLACK:
+                               set_blocktype(arena, cell, BLOCK_WHITE);
+                               if (coalesce) {
+                                       
qcgc_fit_allocator_add(&(arena->cells[last_free_cell]),
+                                                       cell - last_free_cell);
+                               }
+                               free = false;
+                               coalesce = false;
+                               break;
+               }
+       }
+       if (coalesce && !free) {
+               qcgc_fit_allocator_add(&(arena->cells[last_free_cell]),
+                                                       QCGC_ARENA_CELLS_COUNT 
- last_free_cell);
+       }
+       return free;
+}
+
+bool qcgc_arena_is_empty(arena_t *arena) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       for (size_t cell = QCGC_ARENA_FIRST_CELL_INDEX;
+                       cell < QCGC_ARENA_CELLS_COUNT;
+                       cell++) {
+               switch (qcgc_arena_get_blocktype((void *) &arena->cells[cell])) 
{
+                       case BLOCK_WHITE: // Fall through
+                       case BLOCK_BLACK:
+                               return false;
+
+                       default:
+                               break;
+               }
+       }
+       return true;
+}
+
+bool qcgc_arena_is_coalesced(arena_t *arena) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       bool prev_was_free = false;
+       for (size_t cell = QCGC_ARENA_FIRST_CELL_INDEX;
+                       cell < QCGC_ARENA_CELLS_COUNT;
+                       cell++) {
+               switch (qcgc_arena_get_blocktype((void *) &arena->cells[cell])) 
{
+                       case BLOCK_WHITE: // Fall through
+                       case BLOCK_BLACK:
+                               prev_was_free = false;
+                               break;
+
+                       case BLOCK_FREE:
+                               if (prev_was_free) {
+                                       return false;
+                               } else {
+                                       prev_was_free = true;
+                               }
+                               break;
+
+                       case BLOCK_EXTENT:
+                               break;
+               }
+       }
+       return true;
+}
+
+size_t qcgc_arena_free_blocks(arena_t *arena) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       size_t result = 0;
+       for (size_t cell = QCGC_ARENA_FIRST_CELL_INDEX;
+                       cell < QCGC_ARENA_CELLS_COUNT;
+                       cell++) {
+               switch (qcgc_arena_get_blocktype((void *) &arena->cells[cell])) 
{
+                       case BLOCK_WHITE: // Fall through
+                       case BLOCK_BLACK:
+                       case BLOCK_EXTENT:
+                               break;
+
+                       case BLOCK_FREE:
+                               result++;
+                               break;
+               }
+       }
+       return result;
+}
+
+size_t qcgc_arena_white_blocks(arena_t *arena) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       size_t result = 0;
+       for (size_t cell = QCGC_ARENA_FIRST_CELL_INDEX;
+                       cell < QCGC_ARENA_CELLS_COUNT;
+                       cell++) {
+               switch (qcgc_arena_get_blocktype((void *) &arena->cells[cell])) 
{
+                       case BLOCK_BLACK:       // Fall through
+                       case BLOCK_EXTENT:
+                       case BLOCK_FREE:
+                               break;
+
+                       case BLOCK_WHITE:
+                               result++;
+                               break;
+               }
+       }
+       return result;
+}
+
+size_t qcgc_arena_black_blocks(arena_t *arena) {
+#if CHECKED
+       assert(arena != NULL);
+#endif
+       size_t result = 0;
+       for (size_t cell = QCGC_ARENA_FIRST_CELL_INDEX;
+                       cell < QCGC_ARENA_CELLS_COUNT;
+                       cell++) {
+               switch (qcgc_arena_get_blocktype((void *) &arena->cells[cell])) 
{
+                       case BLOCK_WHITE: // Fall through
+                       case BLOCK_FREE:
+                       case BLOCK_EXTENT:
+                               break;
+
+                       case BLOCK_BLACK:
+                               result++;
+                               break;
+               }
+       }
+       return result;
+}
diff --git a/rpython/translator/c/src/qcgc/arena.h 
b/rpython/translator/c/src/qcgc/arena.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/arena.h
@@ -0,0 +1,188 @@
+/**
+ * @file       arena.h
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "gray_stack.h"
+
+#define QCGC_ARENA_SIZE (1<<QCGC_ARENA_SIZE_EXP)
+
+#define QCGC_ARENA_BITMAP_SIZE (1<<(QCGC_ARENA_SIZE_EXP - 7)) // 1 / 128
+#define QCGC_ARENA_CELLS_COUNT (1<<(QCGC_ARENA_SIZE_EXP - 4))
+
+#define QCGC_ARENA_FIRST_CELL_INDEX (1<<(QCGC_ARENA_SIZE_EXP - 10))
+
+/**
+ * @typedef cell_t
+ * The smallest unit of memory that can be addressed and allocated in arenas.
+ */
+typedef uint8_t cell_t[16];
+
+/**
+ * @typedef arena_t
+ * Arena object
+ */
+typedef union {
+       struct {
+               union {
+                       gray_stack_t *gray_stack;
+                       uint8_t block_bitmap[QCGC_ARENA_BITMAP_SIZE];
+               };
+               uint8_t mark_bitmap[QCGC_ARENA_BITMAP_SIZE];
+       };
+       cell_t cells[QCGC_ARENA_CELLS_COUNT];
+} arena_t;
+
+/**
+ * @typedef blocktype_t
+ * Blocktypes:
+ * - BLOCK_EXTENT      Extension of previous block
+ * - BLOCK_FREE                Free block
+ * - BLOCK_WHITE       Allocated block, marked white
+ * - BLOCK_BLACK       Allocated block, marked black
+ */
+typedef enum blocktype {
+       BLOCK_EXTENT,
+       BLOCK_FREE,
+       BLOCK_WHITE,
+       BLOCK_BLACK,
+} blocktype_t;
+
+/**
+ * Create a new arena.
+ *
+ * @return Pointer to new arena, NULL in case of errors
+ */
+arena_t *qcgc_arena_create(void);
+
+/**
+ * Destroys an arena (return to OS).
+ *
+ * @param      arena   The arena to destroy
+ */
+void qcgc_arena_destroy(arena_t *arena);
+
+/**
+ * Arena pointer for given cell.
+ *
+ * @param      ptr             Pointer to cell for which you want to know the 
corresponding
+ *                                     arena
+ * @return     The arena the pointer belongs to
+ */
+arena_t *qcgc_arena_addr(cell_t *ptr);
+
+/**
+ * Index of cell in arena.
+ *
+ * @param      ptr             Pointer to cell for which you want to know the 
cell index
+ * @return     Index of the cell to which ptr points to
+ */
+size_t qcgc_arena_cell_index(cell_t *ptr);
+
+/**
+ * Get bitmap value for given bitmap and cell index.
+ *
+ * @param      bitmap  Bitmap
+ * @param      index   Index of cell
+ * @return     true if bitmap entry is set, false otherwise
+ */
+bool qcgc_arena_get_bitmap_entry(uint8_t *bitmap, size_t index);
+
+/**
+ * Set bitmap value for given bitmap and cell index.
+ *
+ * @param      bitmap  Bitmap
+ * @param      index   Index of cell
+ * @param      value   true to set entry, false to reset entry
+ */
+void qcgc_arena_set_bitmap_entry(uint8_t *bitmap, size_t index, bool value);
+
+/**
+ * Get blocktype.
+ *
+ * @param      ptr             Pointer to cell for which you want to know the 
blocktype
+ * @return     Blocktype
+ */
+blocktype_t qcgc_arena_get_blocktype(cell_t *ptr);
+
+/**
+ * Set blocktype.
+ *
+ * @param      ptr             Pointer to cell for which you want to set the 
blocktype
+ * @param      type    Blocktype that should be set
+ */
+void qcgc_arena_set_blocktype(cell_t *ptr, blocktype_t type);
+
+/**
+ * Mark ptr as allocated area with given size.
+ *
+ * @param      ptr             Pointer to first cell of area
+ * @param      cells   Size in cells
+ */
+void qcgc_arena_mark_allocated(cell_t *ptr, size_t cells);
+
+/**
+ * Mark cell ptr point to as free (no coalescing).
+ *
+ * @param      ptr             Pointer to cell that should be marked as free
+ */
+void qcgc_arena_mark_free(cell_t *ptr);
+
+/**
+ * Sweep given arena.
+ *
+ * @param      arena   Arena
+ * @return     Whether arena is empty after sweeping
+ */
+bool qcgc_arena_sweep(arena_t *arena);
+
+/*******************************************************************************
+ * Debug functions                                                             
*
+ 
******************************************************************************/
+
+/**
+ * Check whether arena is empty.
+ *
+ * @param      arena   Arena
+ * @return     true iff given arena is empty
+ */
+bool qcgc_arena_is_empty(arena_t *arena);
+
+/**
+ * Check whether arena is coalesced (no consecutive free blocks).
+ *
+ * @param      arena   Arena
+ * @return     true iff given arena is coalesced
+ */
+bool qcgc_arena_is_coalesced(arena_t *arena);
+
+/**
+ * Count free blocks.
+ *
+ * @param      arena   Arena
+ * @return     Number of free blocks
+ */
+size_t qcgc_arena_free_blocks(arena_t *arena);
+
+/**
+ * Count white blocks.
+ *
+ * @param      arena   Arena
+ * @return     Number of white blocks
+ */
+size_t qcgc_arena_white_blocks(arena_t *arena);
+
+/**
+ * Count black blocks.
+ *
+ * @param      arena   Arena
+ * @return     Number of black blocks
+ */
+size_t qcgc_arena_black_blocks(arena_t *arena);
diff --git a/rpython/translator/c/src/qcgc/bag.c 
b/rpython/translator/c/src/qcgc/bag.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/bag.c
@@ -0,0 +1,7 @@
+#include "bag.h"
+
+#include <assert.h>
+
+DEFINE_BAG(arena_bag, arena_t *);
+DEFINE_BAG(linear_free_list, cell_t *);
+DEFINE_BAG(exp_free_list, struct exp_free_list_item_s);
diff --git a/rpython/translator/c/src/qcgc/bag.h 
b/rpython/translator/c/src/qcgc/bag.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/bag.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "arena.h"
+
+#define DECLARE_BAG(name, type)                                                
                                                \
+typedef struct name##_s {                                                      
                                                \
+       size_t size;                                                            
                                                        \
+       size_t count;                                                           
                                                        \
+       type items[];                                                           
                                                        \
+} name##_t;                                                                    
                                                                \
+                                                                               
                                                                        \
+name##_t *qcgc_##name##_create(size_t size);                                   
                        \
+name##_t *qcgc_##name##_add(name##_t *self, type item);                        
                        \
+name##_t *qcgc_##name##_remove_index(name##_t *self, size_t index);
+
+#define DEFINE_BAG(name, type)                                                 
                                        \
+QCGC_STATIC size_t name##_size(size_t size);                                   
                        \
+QCGC_STATIC name##_t *name##_grow(name##_t *self);                             
                        \
+QCGC_STATIC name##_t *name##_shrink(name##_t *self);                           
                \
+                                                                               
                                                                        \
+name##_t *qcgc_##name##_create(size_t size) {                                  
                        \
+       name##_t *result = (name##_t *) malloc(name##_size(size));              
                \
+       result->size = size;                                                    
                                                \
+       result->count = 0;                                                      
                                                        \
+       return result;                                                          
                                                        \
+}                                                                              
                                                                        \
+                                                                               
                                                                        \
+name##_t *qcgc_##name##_add(name##_t *self, type item) {                       
                \
+       if (self->count >= self->size) {                                        
                                        \
+               self = name##_grow(self);                                       
                                                \
+       }                                                                       
                                                                        \
+       self->items[self->count++] = item;                                      
                                        \
+       return self;                                                            
                                                        \
+}                                                                              
                                                                        \
+                                                                               
                                                                        \
+name##_t *qcgc_##name##_remove_index(name##_t *self, size_t index) {           
\
+       if (index + 1 < self->count) {                                          
                                        \
+               self->items[index] = self->items[self->count - 1];              
                        \
+       }                                                                       
                                                                        \
+       self->count--;                                                          
                                                        \
+                                                                               
                                                                        \
+       if (self->count < self->size / 4) {                                     
                                        \
+               self = name##_shrink(self);                                     
                                                \
+       }                                                                       
                                                                        \
+       return self;                                                            
                                                        \
+}                                                                              
                                                                        \
+                                                                               
                                                                        \
+QCGC_STATIC name##_t *name##_grow(name##_t *self) {                            
                                \
+       name##_t *new_self = (name##_t *) realloc(self,                         
                        \
+                       name##_size(self->size * 2));                           
                                        \
+       assert(new_self != NULL);                                               
                                                \
+       self = new_self;                                                        
                                                        \
+       self->size *= 2;                                                        
                                                        \
+       return self;                                                            
                                                        \
+}                                                                              
                                                                        \
+                                                                               
                                                                        \
+QCGC_STATIC name##_t *name##_shrink(name##_t *self) {                          
                        \
+       name##_t *new_self = (name##_t *) realloc(self,                         
                        \
+                       name##_size(self->size / 2));                           
                                        \
+       assert(new_self != NULL);                                               
                                                \
+       self = new_self;                                                        
                                                        \
+       self->size /= 2;                                                        
                                                        \
+       return self;                                                            
                                                        \
+}                                                                              
                                                                        \
+                                                                               
                                                                        \
+QCGC_STATIC size_t name##_size(size_t size) {                                  
                                \
+       return sizeof(name##_t) + size * sizeof(type);                          
                        \
+}
+
+struct exp_free_list_item_s {
+       cell_t *ptr;
+       size_t size;
+};
+
+DECLARE_BAG(arena_bag, arena_t *);
+DECLARE_BAG(linear_free_list, cell_t *);
+DECLARE_BAG(exp_free_list, struct exp_free_list_item_s);
diff --git a/rpython/translator/c/src/qcgc/config.h 
b/rpython/translator/c/src/qcgc/config.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/config.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#define CHECKED 1                                                      // 
Enable runtime sanity checks
+
+#define QCGC_INIT_ZERO 1                                       // Init new 
objects with zero bytes
+
+/**
+ * Event logger
+ */
+#define EVENT_LOG 1                                                    // 
Enable event log
+#define LOGFILE "./qcgc_events.log"                    // Default logfile
+#define LOG_ALLOCATION 0                                       // Enable 
allocation log (warning:
+                                                                               
        // significant performance impact)
+
+#define QCGC_SHADOWSTACK_SIZE 128                      // Number of initial 
entries for
+                                                                               
        // shadow stack
+#define QCGC_ARENA_BAG_INIT_SIZE 16                    // Initial size of the 
arena bag
+#define QCGC_ARENA_SIZE_EXP 20                         // Between 16 (64kB) 
and 20 (1MB)
+#define QCGC_LARGE_ALLOC_THRESHOLD 1<<14
+#define QCGC_MARK_LIST_SEGMENT_SIZE 64         // TODO: Tune for performance
+#define QCGC_GRAY_STACK_INIT_SIZE 128          // TODO: Tune for performance
+#define QCGC_INC_MARK_MIN 64                           // TODO: Tune for 
performance
+
+/**
+ * Fit allocator
+ */
+#define QCGC_LARGE_FREE_LIST_FIRST_EXP 5       // First exponent of large free 
list
+#define QCGC_LARGE_FREE_LIST_INIT_SIZE 4       // Initial size for large free 
lists
+#define QCGC_SMALL_FREE_LIST_INIT_SIZE 16      // Initial size for small free 
lists
+
+/**
+ * DO NOT MODIFY BELOW HERE
+ */
+
+#ifdef TESTING
+#define QCGC_STATIC
+#else
+#define QCGC_STATIC static
+#endif
diff --git a/rpython/translator/c/src/qcgc/event_logger.c 
b/rpython/translator/c/src/qcgc/event_logger.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/event_logger.c
@@ -0,0 +1,81 @@
+#include "event_logger.h"
+
+#include <assert.h>
+#include <time.h>
+#include <stdio.h>
+
+#if EVENT_LOG
+static struct {
+       FILE *logfile;
+} event_logger_state;
+
+#endif
+
+void qcgc_event_logger_initialize(void) {
+#if EVENT_LOG
+       event_logger_state.logfile = fopen(LOGFILE, "w");
+       qcgc_event_logger_log(EVENT_LOG_START, 0, NULL);
+
+       if (event_logger_state.logfile == NULL)  {
+               fprintf(stderr, "%s\n", "Failed to create logfile.");
+       }
+#endif
+}
+
+void qcgc_event_logger_destroy(void) {
+#if EVENT_LOG
+       qcgc_event_logger_log(EVENT_LOG_STOP, 0, NULL);
+
+       if (event_logger_state.logfile != NULL) {
+               fflush(event_logger_state.logfile);
+               fclose(event_logger_state.logfile);
+               event_logger_state.logfile = NULL;
+       }
+#endif
+}
+
+void qcgc_event_logger_log(enum event_e event, uint32_t additional_data_size,
+               uint8_t *additional_data) {
+#if EVENT_LOG
+#if CHECKED
+       assert((additional_data_size == 0) == (additional_data == NULL));
+#endif // CHECKED
+       struct {
+               uint32_t sec;
+               uint32_t nsec;
+               uint8_t event_id;
+               uint32_t additional_data_size;
+       } __attribute__ ((packed)) log_entry;
+
+       if (event_logger_state.logfile != NULL) {
+               struct timespec t;
+               clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t);
+
+               log_entry.sec = (uint32_t) t.tv_sec;
+               log_entry.nsec = (uint32_t) t.tv_nsec;
+               log_entry.event_id = (uint8_t) event;
+               log_entry.additional_data_size = additional_data_size;
+
+               // The size and nmemb fields are flipped intentionally
+               int result = 0;
+               result = fwrite(&log_entry, sizeof(log_entry), 1,
+                               event_logger_state.logfile);
+               if (result != 1) {
+                       fprintf(stderr, "%s\n", "Failed to write log entry.");
+                       event_logger_state.logfile = NULL;
+                       return;
+               }
+               if (additional_data_size > 0) {
+                       result = fwrite(additional_data, additional_data_size, 
1,
+                                       event_logger_state.logfile);
+
+                       if (result != 1) {
+                               fprintf(stderr, "%s\n", "Failed to write 
additional data.");
+                               event_logger_state.logfile = NULL;
+                               return;
+                       }
+               }
+       }
+
+#endif // EVENT_LOG
+}
diff --git a/rpython/translator/c/src/qcgc/event_logger.h 
b/rpython/translator/c/src/qcgc/event_logger.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/event_logger.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * All events
+ */
+enum event_e {
+       EVENT_LOG_START,                        // = 0
+       EVENT_LOG_STOP,
+
+       EVENT_SWEEP_START,
+       EVENT_SWEEP_DONE,
+
+       EVENT_ALLOCATE_START,
+       EVENT_ALLOCATE_DONE,            // = 5
+
+       EVENT_NEW_ARENA,
+
+       EVENT_MARK_START,
+       EVENT_INCMARK_START,
+       EVENT_MARK_DONE,
+};
+
+/**
+ * Initialize logger
+ */
+void qcgc_event_logger_initialize(void);
+
+/**
+ * Destroy logger
+ */
+void qcgc_event_logger_destroy(void);
+
+/**
+ * Log event
+ */
+void qcgc_event_logger_log(enum event_e event, uint32_t additional_data_size,
+               uint8_t *additional_data);
diff --git a/rpython/translator/c/src/qcgc/gc_state.h 
b/rpython/translator/c/src/qcgc/gc_state.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/gc_state.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "shadow_stack.h"
+
+/**
+ * @typedef gc_state_t
+ * Garbage collection states.
+ * - GC_PAUSE  No gc in progress
+ * - GC_MARK   Currently marking
+ * - GC_COLLECT        Currently collecting
+ */
+typedef enum gc_phase {
+       GC_PAUSE,
+       GC_MARK,
+       GC_COLLECT,
+} gc_phase_t;
+
+/**
+ * @var qcgc_state
+ *
+ * Global state of the garbage collector
+ */
+struct qcgc_state {
+       shadow_stack_t *shadow_stack;
+       size_t gray_stack_size;
+       gc_phase_t phase;
+} qcgc_state;
diff --git a/rpython/translator/c/src/qcgc/gray_stack.c 
b/rpython/translator/c/src/qcgc/gray_stack.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/gray_stack.c
@@ -0,0 +1,60 @@
+#include "gray_stack.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "gc_state.h"
+
+QCGC_STATIC size_t gray_stack_size(size_t size);
+QCGC_STATIC gray_stack_t *gray_stack_grow(gray_stack_t *stack);
+QCGC_STATIC gray_stack_t *gray_stack_shrink(gray_stack_t *stack);
+
+gray_stack_t *qcgc_gray_stack_create(size_t size) {
+       gray_stack_t *result = (gray_stack_t *) malloc(gray_stack_size(size));
+       result->size = size;
+       result->index = 0;
+       return result;
+}
+
+gray_stack_t *qcgc_gray_stack_push(gray_stack_t *stack, object_t *item) {
+       if (stack->size == stack->index) {
+               stack = gray_stack_grow(stack);
+       }
+       stack->items[stack->index] = item;
+       stack->index++;
+       qcgc_state.gray_stack_size++;
+       return stack;
+}
+
+object_t *qcgc_gray_stack_top(gray_stack_t *stack) {
+#if CHECKED
+       assert(stack->index != 0);
+#endif
+       return stack->items[stack->index - 1];
+}
+
+gray_stack_t *qcgc_gray_stack_pop(gray_stack_t *stack) {
+       // TODO: Add lower bound for size (config.h)
+       if (stack->index < stack->size / 4) {
+               stack = gray_stack_shrink(stack);
+       }
+       stack->index--;
+       qcgc_state.gray_stack_size--;
+       return stack;
+}
+
+QCGC_STATIC size_t gray_stack_size(size_t size) {
+       return (sizeof(gray_stack_t) + size * sizeof(object_t *));
+}
+
+QCGC_STATIC gray_stack_t *gray_stack_grow(gray_stack_t *stack) {
+       stack = (gray_stack_t *) realloc(stack, gray_stack_size(stack->size * 
2));
+       stack->size *= 2;
+       return stack;
+}
+
+QCGC_STATIC gray_stack_t *gray_stack_shrink(gray_stack_t *stack) {
+       stack = (gray_stack_t *) realloc(stack, gray_stack_size(stack->size / 
2));
+       stack->size /= 2;
+       return stack;
+}
diff --git a/rpython/translator/c/src/qcgc/gray_stack.h 
b/rpython/translator/c/src/qcgc/gray_stack.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/gray_stack.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "config.h"
+
+#include <stddef.h>
+
+#include "object.h"
+
+typedef struct gray_stack_s {
+       size_t index;
+       size_t size;
+       object_t *items[];
+} gray_stack_t;
+
+gray_stack_t *qcgc_gray_stack_create(size_t size);
+
+gray_stack_t *qcgc_gray_stack_push(gray_stack_t *stack, object_t *item);
+object_t *qcgc_gray_stack_top(gray_stack_t *stack);
+gray_stack_t *qcgc_gray_stack_pop(gray_stack_t *stack);
diff --git a/rpython/translator/c/src/qcgc/mark_list.c 
b/rpython/translator/c/src/qcgc/mark_list.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/mark_list.c
@@ -0,0 +1,180 @@
+#include "mark_list.h"
+
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+QCGC_STATIC mark_list_t *qcgc_mark_list_grow(mark_list_t *list);
+QCGC_STATIC void qcgc_mark_list_check_invariant(mark_list_t *list);
+
+mark_list_t *qcgc_mark_list_create(size_t initial_size) {
+       size_t length = (initial_size + QCGC_MARK_LIST_SEGMENT_SIZE - 1) / 
QCGC_MARK_LIST_SEGMENT_SIZE;
+       length += (size_t) length == 0;
+       mark_list_t *result = (mark_list_t *)
+               malloc(sizeof(mark_list_t) + length * sizeof(object_t **));
+       result->head = 0;
+       result->tail = 0;
+       result->length = length;
+       result->insert_index = 0;
+       result->count = 0;
+       result->segments[result->head] = (object_t **)
+               calloc(QCGC_MARK_LIST_SEGMENT_SIZE, sizeof(object_t *));
+#if CHECKED
+       qcgc_mark_list_check_invariant(result);
+#endif
+       return result;
+}
+
+void qcgc_mark_list_destroy(mark_list_t *list) {
+#if CHECKED
+       qcgc_mark_list_check_invariant(list);
+#endif
+
+       size_t i = list->head;
+       while (i != list->tail) {
+               free(list->segments[i]);
+               i = (i + 1) % list->length;
+       }
+       free(list->segments[list->tail]);
+       free(list);
+}
+
+mark_list_t *qcgc_mark_list_push(mark_list_t *list, object_t *object) {
+#if CHECKED
+       assert(list != NULL);
+       assert(object != NULL);
+
+       qcgc_mark_list_check_invariant(list);
+       size_t old_count = list->count;
+#endif
+       if (list->insert_index >= QCGC_MARK_LIST_SEGMENT_SIZE) {
+               if ((list->tail + 1) % list->length == list->head) {
+                       list = qcgc_mark_list_grow(list);
+               }
+               list->insert_index = 0;
+               list->tail = (list->tail + 1) % list->length;
+               list->segments[list->tail] = (object_t **)
+                       calloc(QCGC_MARK_LIST_SEGMENT_SIZE, sizeof(object_t *));
+       }
+       list->segments[list->tail][list->insert_index] = object;
+       list->insert_index++;
+       list->count++;
+#if CHECKED
+       assert(list->count == old_count + 1);
+       assert(list->segments[list->tail][list->insert_index - 1] == object);
+       qcgc_mark_list_check_invariant(list);
+#endif
+       return list;
+}
+
+mark_list_t *qcgc_mark_list_push_all(mark_list_t *list,
+               object_t **objects, size_t count) {
+#if CHECKED
+       assert(list != NULL);
+       assert(objects != NULL);
+
+       qcgc_mark_list_check_invariant(list);
+
+       size_t old_count = list->count;
+       for (size_t i = 0; i < count; i++) {
+               assert(objects[i] != NULL);
+       }
+#endif
+       // FIXME: Optimize or remove
+       for (size_t i = 0; i < count; i++) {
+               list = qcgc_mark_list_push(list, objects[i]);
+       }
+#if CHECKED
+       assert(list->count == old_count + count);
+       qcgc_mark_list_check_invariant(list);
+#endif
+       return list;
+}
+
+object_t **qcgc_mark_list_get_head_segment(mark_list_t *list) {
+#if CHECKED
+       assert(list != NULL);
+       assert(list->segments[list->head] != NULL);
+       qcgc_mark_list_check_invariant(list);
+#endif
+       return list->segments[list->head];
+}
+
+mark_list_t *qcgc_mark_list_drop_head_segment(mark_list_t *list) {
+#if CHECKED
+       assert(list != NULL);
+       size_t old_head = list->head;
+       size_t old_tail = list->tail;
+       qcgc_mark_list_check_invariant(list);
+#endif
+       if (list->head != list->tail) {
+               free(list->segments[list->head]);
+               list->segments[list->head] = NULL;
+               list->head = (list->head + 1) % list->length;
+               list->count -= QCGC_MARK_LIST_SEGMENT_SIZE;
+       } else {
+               memset(list->segments[list->head], 0,
+                               sizeof(object_t *) * 
QCGC_MARK_LIST_SEGMENT_SIZE);
+               list->insert_index = 0;
+               list->count = 0;
+       }
+#if CHECKED
+       assert(old_tail == list->tail);
+       if (old_head == old_tail) {
+               assert(old_head == list->head);
+       } else {
+               assert((old_head + 1) % list->length == list->head);
+       }
+       qcgc_mark_list_check_invariant(list);
+#endif
+       return list;
+}
+
+QCGC_STATIC mark_list_t *qcgc_mark_list_grow(mark_list_t *list) {
+#if CHECKED
+       assert(list != NULL);
+       size_t old_length = list->length;
+       size_t old_tail = list->tail;
+       qcgc_mark_list_check_invariant(list);
+#endif
+       mark_list_t *new_list = (mark_list_t *) realloc(list,
+                       sizeof(mark_list_t) + 2 * list->length * 
sizeof(object_t **));
+       if (new_list->tail < new_list->head) {
+               memcpy(new_list->segments + new_list->length,
+                               new_list->segments, (new_list->tail + 1) * 
sizeof(object_t **));
+               new_list->tail = new_list->length + new_list->tail;
+       }
+       new_list->length = 2 * new_list->length;
+#if CHECKED
+       assert(new_list->length == 2 * old_length);
+       if (old_tail < new_list->head) {
+               assert(new_list->tail == old_tail + old_length);
+               for (size_t i = 0; i < old_tail; i++) {
+                       assert(new_list->segments[i] == new_list->segments[i + 
old_length]);
+               }
+       } else {
+               assert(new_list->tail == old_tail);
+       }
+       qcgc_mark_list_check_invariant(new_list);
+#endif
+       return new_list;
+}
+
+QCGC_STATIC void qcgc_mark_list_check_invariant(mark_list_t *list) {
+       assert(list->head < list->length);
+       assert(list->tail < list->length);
+       assert(list->count == (list->tail - list->head + list->length) % 
list->length * QCGC_MARK_LIST_SEGMENT_SIZE + list->insert_index);
+       for (size_t i = 0; i < list->length; i++) {
+               if ((list->head <= i && i <= list->tail) || (list->tail < 
list->head &&
+                               (i <= list->tail || i >= list->head))) {
+                       for (size_t j = 0; j < QCGC_MARK_LIST_SEGMENT_SIZE; 
j++) {
+                               if (i != list->tail || j < list->insert_index) {
+                                       assert(list->segments[i][j] != NULL);
+                               } else {
+                                       assert(list->segments[i][j] == NULL);
+                               }
+                       }
+               }
+       }
+}
diff --git a/rpython/translator/c/src/qcgc/mark_list.h 
b/rpython/translator/c/src/qcgc/mark_list.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/mark_list.h
@@ -0,0 +1,35 @@
+/**
+ * @file       mark_list.h
+ *
+ * Object list for marking step
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <stddef.h>
+
+#include "object.h"
+
+/**
+ * Mark list - circular buffer.
+ */
+typedef struct mark_list_s {
+       size_t head;
+       size_t tail;
+       size_t length;
+       size_t count;
+       size_t insert_index;
+       object_t **segments[];
+} mark_list_t;
+
+mark_list_t *qcgc_mark_list_create(size_t initial_size);
+void qcgc_mark_list_destroy(mark_list_t *list);
+
+mark_list_t *qcgc_mark_list_push(mark_list_t *list, object_t *object);
+mark_list_t *qcgc_mark_list_push_all(mark_list_t *list,
+               object_t **objects, size_t count);
+
+object_t **qcgc_mark_list_get_head_segment(mark_list_t *list);
+mark_list_t *qcgc_mark_list_drop_head_segment(mark_list_t *list);
diff --git a/rpython/translator/c/src/qcgc/object.h 
b/rpython/translator/c/src/qcgc/object.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/object.h
@@ -0,0 +1,11 @@
+#pragma once
+
+
+#include "config.h"
+#include <stdint.h>
+
+#define QCGC_GRAY_FLAG 0x01
+
+typedef struct object_s {
+       uint32_t flags;
+} object_t;
diff --git a/rpython/translator/c/src/qcgc/qcgc.c 
b/rpython/translator/c/src/qcgc/qcgc.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/qcgc.c
@@ -0,0 +1,255 @@
+#include "qcgc.h"
+
+#include <assert.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "allocator.h"
+#include "event_logger.h"
+
+// TODO: Eventually move to own header?
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+void qcgc_mark(void);
+void qcgc_mark_all(void);
+void qcgc_mark_incremental(void);
+void qcgc_pop_object(object_t *object);
+void qcgc_push_object(object_t *object);
+void qcgc_sweep(void);
+
+void qcgc_initialize(void) {
+       qcgc_state.shadow_stack = 
qcgc_shadow_stack_create(QCGC_SHADOWSTACK_SIZE);
+       qcgc_state.gray_stack_size = 0;
+       qcgc_state.phase = GC_PAUSE;
+       qcgc_allocator_initialize();
+       qcgc_event_logger_initialize();
+}
+
+void qcgc_destroy(void) {
+       qcgc_event_logger_destroy();
+       qcgc_allocator_destroy();
+       free(qcgc_state.shadow_stack);
+}
+
+/**
+ * Shadow stack
+ */
+void qcgc_shadowstack_push(object_t *object) {
+       qcgc_state.shadow_stack =
+               qcgc_shadow_stack_push(qcgc_state.shadow_stack, object);
+}
+
+object_t *qcgc_shadowstack_pop(void) {
+       object_t *result = qcgc_shadow_stack_top(qcgc_state.shadow_stack);
+       qcgc_state.shadow_stack = 
qcgc_shadow_stack_pop(qcgc_state.shadow_stack);
+       return result;
+}
+
+/*******************************************************************************
+ * Write barrier                                                               
*
+ 
******************************************************************************/
+void qcgc_write(object_t *object) {
+#if CHECKED
+       assert(object != NULL);
+#endif
+       if ((object->flags & QCGC_GRAY_FLAG) == 0) {
+               object->flags |= QCGC_GRAY_FLAG;
+               if (qcgc_state.phase != GC_PAUSE) {
+                       if (qcgc_arena_get_blocktype((cell_t *) object) == 
BLOCK_BLACK) {
+                               // This was black before, push it to gray stack 
again
+                               arena_t *arena = qcgc_arena_addr((cell_t *) 
object);
+                               arena->gray_stack = qcgc_gray_stack_push(
+                                               arena->gray_stack, object);
+                       }
+               }
+       }
+}
+
+/*******************************************************************************
+ * Allocation                                                                  
*
+ 
******************************************************************************/
+
+object_t *qcgc_allocate(size_t size) {
+#if LOG_ALLOCATION
+       qcgc_event_logger_log(EVENT_ALLOCATE_START, sizeof(size_t),
+                       (uint8_t *) &size);
+#endif
+
+       object_t *result = (object_t *) qcgc_allocator_allocate(size);
+       result->flags |= QCGC_GRAY_FLAG;
+
+#if LOG_ALLOCATION
+       qcgc_event_logger_log(EVENT_ALLOCATE_DONE, sizeof(object_t *),
+                       (uint8_t *) &result);
+#endif
+       return result;
+}
+
+/*******************************************************************************
+ * Collection                                                                  
*
+ 
******************************************************************************/
+
+mark_color_t qcgc_get_mark_color(object_t *object) {
+#if CHECKED
+       assert(object != NULL);
+#endif
+       blocktype_t blocktype = qcgc_arena_get_blocktype((cell_t *) object);
+       bool gray = (object->flags & QCGC_GRAY_FLAG) == QCGC_GRAY_FLAG;
+       if (blocktype == BLOCK_WHITE) {
+               if (gray) {
+                       return MARK_COLOR_LIGHT_GRAY;
+               } else {
+                       return MARK_COLOR_WHITE;
+               }
+       } else if(blocktype == BLOCK_BLACK) {
+               if (gray) {
+                       return MARK_COLOR_DARK_GRAY;
+               } else {
+                       return MARK_COLOR_BLACK;
+               }
+       } else {
+#if CHECKED
+               assert(false);
+#endif
+       }
+}
+
+void qcgc_mark(void) {
+       qcgc_mark_all();
+}
+
+void qcgc_mark_all(void) {
+#if CHECKED
+       assert(qcgc_state.phase == GC_PAUSE || qcgc_state.phase == GC_MARK);
+#endif
+       qcgc_event_logger_log(EVENT_MARK_START, 0, NULL);
+
+       qcgc_state.phase = GC_MARK;
+
+       // Push all roots
+       for (size_t i = 0; i < qcgc_state.shadow_stack->count; i++) {
+               qcgc_push_object(qcgc_state.shadow_stack->items[i]);
+       }
+
+       while(qcgc_state.gray_stack_size > 0) {
+               for (size_t i = 0; i < qcgc_allocator_state.arenas->count; i++) 
{
+                       arena_t *arena = qcgc_allocator_state.arenas->items[i];
+                       while (arena->gray_stack->index > 0) {
+                               object_t *top =
+                                       qcgc_gray_stack_top(arena->gray_stack);
+                               arena->gray_stack =
+                                       qcgc_gray_stack_pop(arena->gray_stack);
+                               qcgc_pop_object(top);
+                       }
+               }
+       }
+
+       qcgc_state.phase = GC_COLLECT;
+
+       qcgc_event_logger_log(EVENT_MARK_DONE, 0, NULL);
+}
+
+void qcgc_mark_incremental(void) {
+#if CHECKED
+       assert(qcgc_state.phase == GC_PAUSE || qcgc_state.phase == GC_MARK);
+#endif
+       unsigned long gray_stack_size = qcgc_state.gray_stack_size;
+       qcgc_event_logger_log(EVENT_INCMARK_START, sizeof(gray_stack_size),
+                       (uint8_t *) &gray_stack_size);
+
+       qcgc_state.phase = GC_MARK;
+
+       // Push all roots
+       for (size_t i = 0; i < qcgc_state.shadow_stack->count; i++) {
+               qcgc_push_object(qcgc_state.shadow_stack->items[i]);
+       }
+
+       for (size_t i = 0; i < qcgc_allocator_state.arenas->count; i++) {
+               arena_t *arena = qcgc_allocator_state.arenas->items[i];
+               size_t initial_stack_size = arena->gray_stack->index;
+               size_t to_process = MIN(arena->gray_stack->index,
+                               MAX(initial_stack_size / 2, QCGC_INC_MARK_MIN));
+               while (to_process > 0) {
+                       object_t *top =
+                               qcgc_gray_stack_top(arena->gray_stack);
+                       arena->gray_stack =
+                               qcgc_gray_stack_pop(arena->gray_stack);
+                       qcgc_pop_object(top);
+                       to_process--;
+               }
+       }
+
+       if (qcgc_state.gray_stack_size == 0) {
+               qcgc_state.phase = GC_COLLECT;
+       }
+
+       gray_stack_size = qcgc_state.gray_stack_size;
+       qcgc_event_logger_log(EVENT_INCMARK_START, sizeof(gray_stack_size),
+                       (uint8_t *) &gray_stack_size);
+}
+
+void qcgc_pop_object(object_t *object) {
+#if CHECKED
+       assert(object != NULL);
+       assert((object->flags & QCGC_GRAY_FLAG) == QCGC_GRAY_FLAG);
+       assert(qcgc_arena_get_blocktype((cell_t *) object) == BLOCK_BLACK);
+#endif
+       object->flags &= ~QCGC_GRAY_FLAG;
+       qcgc_trace_cb(object, &qcgc_push_object);
+#if CHECKED
+       assert(qcgc_get_mark_color(object) == MARK_COLOR_BLACK);
+#endif
+}
+
+void qcgc_push_object(object_t *object) {
+#if CHECKED
+       size_t old_stack_size = qcgc_state.gray_stack_size;
+       assert(qcgc_state.phase == GC_MARK);
+#endif
+       if (object != NULL) {
+               if (qcgc_arena_get_blocktype((cell_t *) object) == BLOCK_WHITE) 
{
+                       object->flags |= QCGC_GRAY_FLAG;
+                       qcgc_arena_set_blocktype((cell_t *) object, 
BLOCK_BLACK);
+                       arena_t *arena = qcgc_arena_addr((cell_t *) object);
+                       arena->gray_stack = 
qcgc_gray_stack_push(arena->gray_stack, object);
+               }
+       }
+#if CHECKED
+       if (object != NULL) {
+               if (old_stack_size == qcgc_state.gray_stack_size) {
+                       assert(qcgc_get_mark_color(object) == MARK_COLOR_BLACK 
||
+                                       qcgc_get_mark_color(object) == 
MARK_COLOR_DARK_GRAY);
+               } else {
+                       assert(qcgc_state.gray_stack_size == old_stack_size + 
1);
+                       assert(qcgc_get_mark_color(object) == 
MARK_COLOR_DARK_GRAY);
+               }
+       } else {
+               assert(old_stack_size == qcgc_state.gray_stack_size);
+       }
+#endif
+}
+
+void qcgc_sweep(void) {
+#if CHECKED
+       assert(qcgc_state.phase == GC_COLLECT);
+#endif
+       unsigned long arena_count;
+       arena_count = qcgc_allocator_state.arenas->count;
+       qcgc_event_logger_log(EVENT_SWEEP_START, sizeof(arena_count),
+                       (uint8_t *) &arena_count);
+
+       for (size_t i = 0; i < qcgc_allocator_state.arenas->count; i++) {
+               qcgc_arena_sweep(qcgc_allocator_state.arenas->items[i]);
+       }
+       qcgc_state.phase = GC_PAUSE;
+
+       qcgc_event_logger_log(EVENT_SWEEP_DONE, 0, NULL);
+}
+
+void qcgc_collect(void) {
+       qcgc_mark();
+       qcgc_sweep();
+}
diff --git a/rpython/translator/c/src/qcgc/qcgc.h 
b/rpython/translator/c/src/qcgc/qcgc.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/qcgc.h
@@ -0,0 +1,93 @@
+/**
+ * @file       qcgc.h
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "arena.h"
+#include "gc_state.h"
+#include "gray_stack.h"
+#include "object.h"
+
+/**
+ * @typedef mark_color
+ * Object state during collection
+ * - MARK_COLOR_WHITE          Clean and unprocessed
+ * - MARK_COLOR_LIGHT_GRAY     Dirty and unprocessed
+ * - MARK_COLOR_DARK_GRAY      Processing
+ * - MARK_COLOR_BLACK          Processed
+ */
+typedef enum mark_color {
+       MARK_COLOR_WHITE,
+       MARK_COLOR_LIGHT_GRAY,
+       MARK_COLOR_DARK_GRAY,
+       MARK_COLOR_BLACK,
+} mark_color_t;
+
+/**
+ * Initialize the garbage collector.
+ */
+void qcgc_initialize(void);
+
+/**
+ * Destroy the garbage collector.
+ */
+void qcgc_destroy(void);
+
+/**
+ * Write barrier.
+ *
+ * @param      object  Object to write to
+ */
+void qcgc_write(object_t *object);
+
+/**
+ * Allocate new memory region
+ *
+ * @param      size    Desired size of the memory region
+ * @return     Pointer to memory large enough to hold size bytes, NULL in case 
of
+ *                     errors
+ */
+object_t *qcgc_allocate(size_t size);
+
+/**
+ * Run garbage collection.
+ */
+void qcgc_collect(void);
+
+/**
+ * Return color of object.
+ *
+ * @returs The color of the object, according to the mark algorithm.
+ */
+mark_color_t qcgc_get_mark_color(object_t *object);
+
+/**
+ * Add object to shadow stack
+ *
+ * @param      object  The object to push
+ */
+void qcgc_shadowstack_push(object_t *object);
+
+/**
+ * Pop object from shadow stack
+ *
+ * @return     Top element of the shadowstack
+ */
+object_t *qcgc_shadowstack_pop(void);
+
+/**
+ * Tracing function.
+ *
+ * This function traces an object, i.e. calls visit on every object referenced
+ * by the given object. Has to be provided by the library user.
+ *
+ * @param      object  The object to trace
+ * @param      visit   The function to be called on the referenced objects
+ */
+extern void qcgc_trace_cb(object_t *object, void (*visit)(object_t *object));
diff --git a/rpython/translator/c/src/qcgc/shadow_stack.c 
b/rpython/translator/c/src/qcgc/shadow_stack.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/shadow_stack.c
@@ -0,0 +1,56 @@
+#include "shadow_stack.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+QCGC_STATIC size_t shadow_stack_size(size_t size);
+QCGC_STATIC shadow_stack_t *shadow_stack_grow(shadow_stack_t *stack);
+QCGC_STATIC shadow_stack_t *shadow_stack_shrink(shadow_stack_t *stack);
+
+shadow_stack_t *qcgc_shadow_stack_create(size_t size) {
+       shadow_stack_t *result = (shadow_stack_t *) 
malloc(shadow_stack_size(size));
+       result->size = size;
+       result->count = 0;
+       return result;
+}
+
+shadow_stack_t *qcgc_shadow_stack_push(shadow_stack_t *stack, object_t *item) {
+       if (stack->size == stack->count) {
+               stack = shadow_stack_grow(stack);
+       }
+       stack->items[stack->count] = item;
+       stack->count++;
+       return stack;
+}
+
+object_t *qcgc_shadow_stack_top(shadow_stack_t *stack) {
+#if CHECKED
+       assert(stack->count != 0);
+#endif
+       return stack->items[stack->count - 1];
+}
+
+shadow_stack_t *qcgc_shadow_stack_pop(shadow_stack_t *stack) {
+       // TODO: Add lower bound for size (config.h)
+       if (stack->count < stack->size / 4) {
+               stack = shadow_stack_shrink(stack);
+       }
+       stack->count--;
+       return stack;
+}
+
+QCGC_STATIC size_t shadow_stack_size(size_t size) {
+       return (sizeof(shadow_stack_t) + size * sizeof(object_t *));
+}
+
+QCGC_STATIC shadow_stack_t *shadow_stack_grow(shadow_stack_t *stack) {
+       stack = (shadow_stack_t *) realloc(stack, shadow_stack_size(stack->size 
* 2));
+       stack->size *= 2;
+       return stack;
+}
+
+QCGC_STATIC shadow_stack_t *shadow_stack_shrink(shadow_stack_t *stack) {
+       stack = (shadow_stack_t *) realloc(stack, shadow_stack_size(stack->size 
/ 2));
+       stack->size /= 2;
+       return stack;
+}
diff --git a/rpython/translator/c/src/qcgc/shadow_stack.h 
b/rpython/translator/c/src/qcgc/shadow_stack.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/qcgc/shadow_stack.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "config.h"
+
+#include <stddef.h>
+
+#include "object.h"
+
+typedef struct shadow_stack_s {
+       size_t count;
+       size_t size;
+       object_t *items[];
+} shadow_stack_t;
+
+shadow_stack_t *qcgc_shadow_stack_create(size_t size);
+
+shadow_stack_t *qcgc_shadow_stack_push(shadow_stack_t *stack, object_t *item);
+object_t *qcgc_shadow_stack_top(shadow_stack_t *stack);
+shadow_stack_t *qcgc_shadow_stack_pop(shadow_stack_t *stack);
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to