heap.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #include <stdio.h>
  2. #include <stdbool.h>
  3. #include <string.h>
  4. #include "./heap.h"
  5. uintptr_t heap[HEAP_CAP_WORDS] = {0};
  6. const uintptr_t *stack_base = 0;
  7. bool reachable_chunks[CHUNK_LIST_CAP] = {0};
  8. void *to_free[CHUNK_LIST_CAP] = {0};
  9. size_t to_free_count = 0;
  10. Chunk_List alloced_chunks = {0};
  11. Chunk_List freed_chunks = {
  12. .count = 1,
  13. .chunks = {
  14. [0] = {.start = heap, .size = sizeof(heap)}
  15. },
  16. };
  17. Chunk_List tmp_chunks = {0};
  18. void chunk_list_insert(Chunk_List *list, void *start, size_t size)
  19. {
  20. assert(list->count < CHUNK_LIST_CAP);
  21. list->chunks[list->count].start = start;
  22. list->chunks[list->count].size = size;
  23. for (size_t i = list->count;
  24. i > 0 && list->chunks[i].start < list->chunks[i - 1].start;
  25. --i) {
  26. const Chunk t = list->chunks[i];
  27. list->chunks[i] = list->chunks[i - 1];
  28. list->chunks[i - 1] = t;
  29. }
  30. list->count += 1;
  31. }
  32. void chunk_list_merge(Chunk_List *dst, const Chunk_List *src)
  33. {
  34. dst->count = 0;
  35. for (size_t i = 0; i < src->count; ++i) {
  36. const Chunk chunk = src->chunks[i];
  37. if (dst->count > 0) {
  38. Chunk *top_chunk = &dst->chunks[dst->count - 1];
  39. if (top_chunk->start + top_chunk->size == chunk.start) {
  40. top_chunk->size += chunk.size;
  41. } else {
  42. chunk_list_insert(dst, chunk.start, chunk.size);
  43. }
  44. } else {
  45. chunk_list_insert(dst, chunk.start, chunk.size);
  46. }
  47. }
  48. }
  49. void chunk_list_dump(const Chunk_List *list, const char *name)
  50. {
  51. printf("%s Chunks (%zu):\n", name, list->count);
  52. for (size_t i = 0; i < list->count; ++i) {
  53. printf(" start: %p, size: %zu\n",
  54. (void*) list->chunks[i].start,
  55. list->chunks[i].size);
  56. }
  57. }
  58. int chunk_list_find(const Chunk_List *list, uintptr_t *ptr)
  59. {
  60. for (size_t i = 0; i < list->count; ++i) {
  61. if (list->chunks[i].start == ptr) {
  62. return (int) i;
  63. }
  64. }
  65. return -1;
  66. }
  67. void chunk_list_remove(Chunk_List *list, size_t index)
  68. {
  69. assert(index < list->count);
  70. for (size_t i = index; i < list->count - 1; ++i) {
  71. list->chunks[i] = list->chunks[i + 1];
  72. }
  73. list->count -= 1;
  74. }
  75. void *heap_alloc(size_t size_bytes)
  76. {
  77. const size_t size_words = (size_bytes + sizeof(uintptr_t) - 1) / sizeof(uintptr_t);
  78. if (size_words > 0) {
  79. chunk_list_merge(&tmp_chunks, &freed_chunks);
  80. freed_chunks = tmp_chunks;
  81. for (size_t i = 0; i < freed_chunks.count; ++i) {
  82. const Chunk chunk = freed_chunks.chunks[i];
  83. if (chunk.size >= size_words) {
  84. chunk_list_remove(&freed_chunks, i);
  85. const size_t tail_size_words = chunk.size - size_words;
  86. chunk_list_insert(&alloced_chunks, chunk.start, size_words);
  87. if (tail_size_words > 0) {
  88. chunk_list_insert(&freed_chunks, chunk.start + size_words, tail_size_words);
  89. }
  90. return chunk.start;
  91. }
  92. }
  93. }
  94. return NULL;
  95. }
  96. void heap_free(void *ptr)
  97. {
  98. if (ptr != NULL) {
  99. const int index = chunk_list_find(&alloced_chunks, ptr);
  100. assert(index >= 0);
  101. assert(ptr == alloced_chunks.chunks[index].start);
  102. chunk_list_insert(&freed_chunks,
  103. alloced_chunks.chunks[index].start,
  104. alloced_chunks.chunks[index].size);
  105. chunk_list_remove(&alloced_chunks, (size_t) index);
  106. }
  107. }
  108. static void mark_region(const uintptr_t *start, const uintptr_t *end)
  109. {
  110. for (; start < end; start += 1) {
  111. const uintptr_t *p = (const uintptr_t *) *start;
  112. for (size_t i = 0; i < alloced_chunks.count; ++i) {
  113. Chunk chunk = alloced_chunks.chunks[i];
  114. if (chunk.start <= p && p < chunk.start + chunk.size) {
  115. if (!reachable_chunks[i]) {
  116. reachable_chunks[i] = true;
  117. mark_region(chunk.start, chunk.start + chunk.size);
  118. }
  119. }
  120. }
  121. }
  122. }
  123. void heap_collect()
  124. {
  125. const uintptr_t *stack_start = (const uintptr_t*)__builtin_frame_address(0);
  126. memset(reachable_chunks, 0, sizeof(reachable_chunks));
  127. mark_region(stack_start, stack_base + 1);
  128. to_free_count = 0;
  129. for (size_t i = 0; i < alloced_chunks.count; ++i) {
  130. if (!reachable_chunks[i]) {
  131. assert(to_free_count < CHUNK_LIST_CAP);
  132. to_free[to_free_count++] = alloced_chunks.chunks[i].start;
  133. }
  134. }
  135. for (size_t i = 0; i < to_free_count; ++i) {
  136. heap_free(to_free[i]);
  137. }
  138. }