/core/heap.c

https://github.com/zick/kashiwa · C · 422 lines · 386 code · 30 blank · 6 comment · 83 complexity · 5af1fd5d4da0b95bef0ede5decc24ab1 MD5 · raw file

  1. #include "heap.h"
  2. #include <assert.h>
  3. #include <setjmp.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "object.h"
  7. #define INITIAL_HEAP_SIZE 1048576
  8. #define INITIAL_HEAP_ROOTSET_ENTRY 256
  9. #define INITIAL_HEAP_REMSET_ENTRY 256
  10. #define FREELIST_ELM(x) ((lobject)(x) & 1)
  11. #define FREELIST_NEXT(x) ((lobject)(x) & ~1)
  12. #define MAKE_FREELIST_ELM(x, next) *((lobject*)(x)) = ((lobject)(next) | 1)
  13. unsigned char* heap;
  14. unsigned char* heap_end;
  15. static unsigned char* heap_from;
  16. static unsigned char* heap_to;
  17. static unsigned char* heap_free;
  18. static unsigned char* heap_scan_start;
  19. static size_t heap_size;
  20. static lobject** heap_rootset;
  21. static lobject** heap_rootset_end;
  22. static lobject** heap_rootset_free;
  23. static size_t heap_rootset_size;
  24. static void** heap_remset;
  25. static void** heap_remset_end;
  26. static void** heap_remset_free;
  27. static void** heap_remset_tail;
  28. static size_t heap_remset_size;
  29. static int in_heap_gc;
  30. static jmp_buf stack_gc_entry_point;
  31. static jmp_buf heap_gc_entry_point;
  32. void init_heap() {
  33. heap_size = INITIAL_HEAP_SIZE;
  34. heap = heap_free = (unsigned char*)malloc(heap_size);
  35. heap_end = heap + heap_size;
  36. heap_from = heap;
  37. heap_to = heap + heap_size / 2;
  38. heap_rootset_size = INITIAL_HEAP_ROOTSET_ENTRY * sizeof(lobject*);
  39. heap_rootset = heap_rootset_free = (lobject**)malloc(heap_rootset_size);
  40. heap_rootset_end = heap_rootset + INITIAL_HEAP_ROOTSET_ENTRY;
  41. heap_remset_size = INITIAL_HEAP_REMSET_ENTRY * sizeof(void*);
  42. heap_remset = heap_remset_free = (void**)malloc(heap_remset_size);
  43. heap_remset_end = heap_remset + INITIAL_HEAP_REMSET_ENTRY;
  44. heap_remset_tail = heap_remset_free;
  45. in_heap_gc = 0;
  46. }
  47. static int in_target_space(void* p) {
  48. int dummy;
  49. /* assume that the stack is allocated at the end of the memory. */
  50. if ((void*)&dummy < p) { /* stack and heap gc */
  51. return 1;
  52. }
  53. if (!in_heap_gc) { /* stack gc only */
  54. return 0;
  55. }
  56. /* heap gc only */
  57. if (heap_from <= (unsigned char*)p &&
  58. (unsigned char*)p < heap_from + heap_size / 2) {
  59. return 1;
  60. }
  61. return 0;
  62. }
  63. static lobject copy_lobject(lobject x);
  64. static lobject copy_other_object(void* p) {
  65. lobject ret;
  66. size_t size;
  67. if (!in_target_space(p)) {
  68. return (lobject)ADD_PTAG(p, PTAG_OTHER);
  69. }
  70. switch (OBJ_TAG(p)) {
  71. case TAG_CONT_PROC:
  72. size = sizeof(cont_proc_t);
  73. break;
  74. case TAG_SYMBOL:
  75. size = sizeof(symbol_t);
  76. break;
  77. case TAG_FORWARDING:
  78. if (in_heap_gc && in_target_space(FORWARDING_ADDRESS(p))) {
  79. FORWARDING_ADDRESS(p) = (void*)
  80. REM_PTAG(copy_lobject(ADD_PTAG(FORWARDING_ADDRESS(p), PTAG_OTHER)));
  81. }
  82. return ADD_PTAG(FORWARDING_ADDRESS(p), PTAG_OTHER);
  83. default:
  84. assert(0);
  85. }
  86. #ifdef GC_VERBOSE
  87. fprintf(stderr, "copy %p -> %p (tag:%x)\n", p, heap_free, (int)OBJ_TAG(p));
  88. #endif
  89. if (!in_heap_gc && heap_free + size >= heap_from + heap_size / 2) {
  90. longjmp(stack_gc_entry_point, 1);
  91. } else if (in_heap_gc && heap_free + size >= heap_to + heap_size / 2) {
  92. longjmp(heap_gc_entry_point, 1);
  93. }
  94. memcpy(heap_free, p, size);
  95. OBJ_TAG(p) = TAG_FORWARDING;
  96. FORWARDING_ADDRESS(p) = heap_free;
  97. ret = ADD_PTAG(heap_free, PTAG_OTHER);
  98. heap_free += size;
  99. return ret;
  100. }
  101. static env_t* copy_env(env_t* env);
  102. static lobject copy_lobject(lobject x) {
  103. lobject ret;
  104. size_t size;
  105. void* p;
  106. switch (GET_PTAG(x)) {
  107. case PTAG_CONT:
  108. if (OBJ_TAG(x) == TAG_ENV) { /* Not tagged environment can be come. */
  109. return (lobject)copy_env((env_t*)x);
  110. }
  111. size = sizeof(cont_t);
  112. break;
  113. case PTAG_CONS:
  114. size = sizeof(cons_t);
  115. break;
  116. case PTAG_FIXNUM:
  117. return x;
  118. case PTAG_OTHER:
  119. return copy_other_object((void*)REM_PTAG(x));
  120. default:
  121. assert(0);
  122. }
  123. p = (void*)REM_PTAG(x);
  124. if (!in_target_space(p)) {
  125. return x;
  126. }
  127. if (OBJ_TAG(p) == TAG_FORWARDING) {
  128. if (in_heap_gc && in_target_space(FORWARDING_ADDRESS(p))) {
  129. FORWARDING_ADDRESS(p) = (void*)
  130. REM_PTAG(copy_lobject(ADD_PTAG(FORWARDING_ADDRESS(p), GET_PTAG(x))));
  131. }
  132. return ADD_PTAG(FORWARDING_ADDRESS(p), GET_PTAG(x));
  133. }
  134. #ifdef GC_VERBOSE
  135. fprintf(stderr, "copy %p -> %p (tag:%x)\n", p, heap_free, (int)OBJ_TAG(x));
  136. #endif
  137. if (!in_heap_gc && heap_free + size >= heap_from + heap_size / 2) {
  138. longjmp(stack_gc_entry_point, 1);
  139. } else if (in_heap_gc && heap_free + size >= heap_to + heap_size / 2) {
  140. longjmp(heap_gc_entry_point, 1);
  141. }
  142. memcpy(heap_free, p, size);
  143. OBJ_TAG(p) = TAG_FORWARDING;
  144. FORWARDING_ADDRESS(p) = heap_free;
  145. ret = ADD_PTAG(heap_free, GET_PTAG(x));
  146. heap_free += size;
  147. return ret;
  148. }
  149. static env_t* copy_env(env_t* env) {
  150. env_t* ret;
  151. size_t size;
  152. if (env == NULL || !in_target_space(env)) {
  153. return env;
  154. }
  155. if (OBJ_TAG(env) == TAG_FORWARDING) {
  156. if (in_heap_gc && in_target_space(FORWARDING_ADDRESS(env))) {
  157. FORWARDING_ADDRESS(env) = (void*)copy_env(FORWARDING_ADDRESS(env));
  158. }
  159. return FORWARDING_ADDRESS(env);
  160. }
  161. size = sizeof(env_t) + sizeof(lobject) * (env->num - 1);
  162. #ifdef GC_VERBOSE
  163. fprintf(stderr, "copy %p -> %p (env)\n", env, heap_free);
  164. #endif
  165. if (!in_heap_gc && heap_free + size >= heap_from + heap_size / 2) {
  166. longjmp(stack_gc_entry_point, 1);
  167. } else if (in_heap_gc && heap_free + size >= heap_to + heap_size / 2) {
  168. longjmp(heap_gc_entry_point, 1);
  169. }
  170. memcpy(heap_free, env, size);
  171. OBJ_TAG(env) = TAG_FORWARDING;
  172. FORWARDING_ADDRESS(env) = heap_free;
  173. ret = (env_t*)heap_free;
  174. heap_free += size;
  175. return ret;
  176. }
  177. static void scan_lobject(void* p, size_t* size) {
  178. int i;
  179. switch (OBJ_TAG(p)) {
  180. case TAG_CONT:
  181. *size = sizeof(cont_t);
  182. ((cont_t*)p)->env = copy_env(((cont_t*)p)->env);
  183. break;
  184. case TAG_CONS:
  185. *size = sizeof(cons_t);
  186. ((cons_t*)p)->car = copy_lobject(((cons_t*)p)->car);
  187. ((cons_t*)p)->cdr = copy_lobject(((cons_t*)p)->cdr);
  188. break;
  189. case TAG_ENV:
  190. *size = sizeof(env_t) + sizeof(lobject) * (((env_t*)p)->num - 1);
  191. ((env_t*)p)->link = copy_env(((env_t*)p)->link);
  192. for (i = 0; i < ((env_t*)p)->num; ++i) {
  193. ((env_t*)p)->vars[i] = copy_lobject(((env_t*)p)->vars[i]);
  194. }
  195. break;
  196. case TAG_CONT_PROC:
  197. *size = sizeof(cont_proc_t);
  198. ((cont_proc_t*)p)->c =
  199. (void*)copy_lobject((lobject)((cont_proc_t*)p)->c);
  200. break;
  201. case TAG_SYMBOL:
  202. *size = sizeof(symbol_t);
  203. break;
  204. default:
  205. assert(0);
  206. }
  207. }
  208. static void scan_heap_for_stack_gc() {
  209. unsigned char* p;
  210. size_t size;
  211. for (p = heap_scan_start; p < heap_free;) {
  212. #ifdef GC_VERBOSE
  213. fprintf(stderr, "scan %p (tag:%x)\n", p, OBJ_TAG(p));
  214. #endif
  215. scan_lobject((void*)p, &size);
  216. p += size;
  217. }
  218. }
  219. static void reset_remset() {
  220. heap_remset_free = heap_remset_tail = heap_remset;
  221. }
  222. static void do_stack_gc(thunk_t* thunk) {
  223. int i;
  224. lobject** p;
  225. void** vp;
  226. heap_scan_start = heap_free;
  227. for (p = heap_rootset; p < heap_rootset_free; ++p) {
  228. if (**p) {
  229. **p = copy_lobject(**p);
  230. }
  231. }
  232. for (vp = heap_remset; vp < heap_remset_free; ++vp) {
  233. size_t dummy;
  234. scan_lobject(*vp, &dummy);
  235. }
  236. thunk->env = copy_env(thunk->env);
  237. for (i = 0; i < thunk->num; ++i) {
  238. thunk->vars[i] = copy_lobject(thunk->vars[i]);
  239. }
  240. scan_heap_for_stack_gc();
  241. reset_remset();
  242. }
  243. void stack_gc(thunk_t* thunk) {
  244. assert(!in_heap_gc);
  245. #ifdef GC_VERBOSE
  246. fprintf(stderr, "*** stack gc begin\n");
  247. #endif
  248. if (!setjmp(stack_gc_entry_point)) {
  249. do_stack_gc(thunk);
  250. } else {
  251. heap_gc(thunk);
  252. }
  253. #ifdef GC_VERBOSE
  254. fprintf(stderr, "*** stack gc end\n");
  255. #endif
  256. }
  257. void add_heap_rootset(lobject* root) {
  258. if (heap_rootset_free == heap_rootset_end) {
  259. heap_rootset = (lobject**)realloc(heap_rootset, heap_rootset_size * 2);
  260. if (!heap_rootset) {
  261. fprintf(stderr, "heap rootset is full\n");
  262. exit(1);
  263. }
  264. heap_rootset_free = heap_rootset + heap_rootset_size / sizeof(lobject*);
  265. heap_rootset_size *= 2;
  266. heap_rootset_end = heap_rootset + heap_rootset_size / sizeof(lobject*);
  267. }
  268. *heap_rootset_free = root;
  269. ++heap_rootset_free;
  270. }
  271. void add_heap_remset(void* root) {
  272. *heap_remset_free = root;
  273. if (heap_remset_free == heap_remset_tail) {
  274. ++heap_remset_free;
  275. ++heap_remset_tail;
  276. } else {
  277. heap_remset_free = (void**)FREELIST_NEXT(*heap_remset_free);
  278. }
  279. if (heap_remset_tail == heap_remset_end) {
  280. heap_remset = (void**)realloc(heap_remset, heap_remset_size * 2);
  281. if (!heap_remset) {
  282. fprintf(stderr, "heap remset is full\n");
  283. exit(1);
  284. }
  285. heap_remset_free = heap_remset + heap_remset_size / sizeof(void*);
  286. heap_remset_size *= 2;
  287. heap_remset_end = heap_remset + heap_remset_size / sizeof(void*);
  288. heap_remset_tail = heap_remset_free;
  289. }
  290. }
  291. void write_barrier(void* root, lobject oldval, lobject nextval) {
  292. int dummy, old_in_stack = 0, next_in_stack = 0;
  293. void **p, **position = NULL;
  294. if (GET_PTAG(oldval) != PTAG_FIXNUM && (int*)oldval > &dummy) {
  295. old_in_stack = 1;
  296. }
  297. if (GET_PTAG(nextval) != PTAG_FIXNUM && (int*)nextval > &dummy) {
  298. next_in_stack = 1;
  299. }
  300. if (old_in_stack || next_in_stack) {
  301. for (p = heap_remset; p < heap_remset_tail; ++p) {
  302. if (FREELIST_ELM(*p)) {
  303. continue;
  304. } else if (*p == root) {
  305. position = p;
  306. break;
  307. }
  308. }
  309. }
  310. if (position && !next_in_stack) { /* remove from remember set */
  311. MAKE_FREELIST_ELM(position, heap_remset_free);
  312. heap_remset_free = position;
  313. }
  314. if (!position && next_in_stack) { /* add to remember set */
  315. add_heap_remset(root);
  316. }
  317. }
  318. static void scan_heap_for_heap_gc() {
  319. unsigned char* p;
  320. size_t size;
  321. for (p = heap_to; p < heap_free;) {
  322. #ifdef GC_VERBOSE
  323. fprintf(stderr, "scan %p (tag:%x)\n", p, OBJ_TAG(p));
  324. #endif
  325. scan_lobject((void*)p, &size);
  326. p += size;
  327. }
  328. }
  329. static void do_heap_gc(thunk_t* thunk) {
  330. int i;
  331. lobject** p;
  332. unsigned char* tmp;
  333. heap_free = heap_to;
  334. for (p = heap_rootset; p < heap_rootset_free; ++p) {
  335. if (**p) {
  336. **p = copy_lobject(**p);
  337. }
  338. }
  339. if (thunk) {
  340. thunk->env = copy_env(thunk->env);
  341. for (i = 0; i < thunk->num; ++i) {
  342. thunk->vars[i] = copy_lobject(thunk->vars[i]);
  343. }
  344. }
  345. scan_heap_for_heap_gc();
  346. reset_remset();
  347. tmp = heap_from;
  348. heap_from = heap_to;
  349. heap_to = tmp;
  350. }
  351. void heap_gc(thunk_t* thunk) {
  352. int finished = 0;
  353. unsigned char* prev_heap = NULL;
  354. assert(!in_heap_gc);
  355. in_heap_gc = 1;
  356. #ifdef GC_VERBOSE
  357. fprintf(stderr, "*** heap gc begin\n");
  358. #endif
  359. while (!finished) {
  360. if (!setjmp(heap_gc_entry_point)) {
  361. do_heap_gc(thunk);
  362. finished = 1;
  363. } else {
  364. /* Assign the whole (old) heap to 'from space' and allocate new heap.
  365. * Assign semi-space of the new heap to 'to space' and GC again.
  366. * If the new heap is already allocated, quit Kashiwa.
  367. */
  368. heap_from = heap;
  369. heap_size *= 2; /* TODO: calculate safe size */
  370. heap = (unsigned char*)malloc(heap_size);
  371. if (!heap || prev_heap) {
  372. fprintf(stderr, "heap is full.\n");
  373. exit(1);
  374. }
  375. prev_heap = heap_from;
  376. heap_to = heap + heap_size / 2;
  377. heap_end = heap + heap_size;
  378. #ifdef GC_VERBOSE
  379. fprintf(stderr, "expand heap: %lu bytes -> %lu bytes\n",
  380. heap_size / 2, heap_size);
  381. #endif
  382. }
  383. }
  384. if (prev_heap) {
  385. free(prev_heap);
  386. heap_to = heap; /* do_heap_gc swapped heap_from with heap_to */
  387. }
  388. #ifdef GC_VERBOSE
  389. fprintf(stderr, "*** heap gc end\n");
  390. #endif
  391. in_heap_gc = 0;
  392. }