ems_gc.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. * Copyright (C) 2022 Tencent Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include "ems_gc.h"
  6. #include "ems_gc_internal.h"
  7. #ifndef GB // Some platforms define already, causing build warnings.
  8. #define GB (1 << 30UL)
  9. #endif
  10. #define MARK_NODE_OBJ_CNT 256
  11. #if WASM_ENABLE_GC != 0
  12. /* mark node is used for gc marker*/
  13. typedef struct mark_node_struct {
  14. /* number of to-expand objects can be saved in this node */
  15. gc_size_t cnt;
  16. /* the first unused index */
  17. uint32 idx;
  18. /* next node on the node list */
  19. struct mark_node_struct *next;
  20. /* the actual to-expand objects list */
  21. gc_object_t set[MARK_NODE_OBJ_CNT];
  22. } mark_node_t;
  23. /**
  24. * Alloc a mark node from the native heap
  25. *
  26. * @return a valid mark node if success, NULL otherwise
  27. */
  28. static mark_node_t *
  29. alloc_mark_node(void)
  30. {
  31. mark_node_t *ret = (mark_node_t *)BH_MALLOC(sizeof(mark_node_t));
  32. if (!ret) {
  33. LOG_ERROR("alloc a new mark node failed");
  34. return NULL;
  35. }
  36. ret->cnt = sizeof(ret->set) / sizeof(ret->set[0]);
  37. ret->idx = 0;
  38. ret->next = NULL;
  39. return ret;
  40. }
  41. /* Free a mark node to the native heap
  42. *
  43. * @param node the mark node to free, should not be NULL
  44. */
  45. static void
  46. free_mark_node(mark_node_t *node)
  47. {
  48. bh_assert(node);
  49. BH_FREE((gc_object_t)node);
  50. }
  51. /**
  52. * Sweep phase of mark_sweep algorithm
  53. * @param heap the heap to sweep, should be a valid instance heap
  54. * which has already been marked
  55. */
  56. static void
  57. sweep_instance_heap(gc_heap_t *heap)
  58. {
  59. hmu_t *cur = NULL, *end = NULL, *last = NULL;
  60. hmu_type_t ut;
  61. gc_size_t size;
  62. int i, lsize;
  63. gc_size_t tot_free = 0;
  64. bh_assert(gci_is_heap_valid(heap));
  65. cur = (hmu_t *)heap->base_addr;
  66. last = NULL;
  67. end = (hmu_t *)((char *)heap->base_addr + heap->current_size);
  68. /* reset KFC */
  69. lsize =
  70. (int)(sizeof(heap->kfc_normal_list) / sizeof(heap->kfc_normal_list[0]));
  71. for (i = 0; i < lsize; i++) {
  72. heap->kfc_normal_list[i].next = NULL;
  73. }
  74. heap->kfc_tree_root->right = NULL;
  75. heap->root_set = NULL;
  76. while (cur < end) {
  77. ut = hmu_get_ut(cur);
  78. size = hmu_get_size(cur);
  79. bh_assert(size > 0);
  80. if (ut == HMU_FC || ut == HMU_FM
  81. || (ut == HMU_VO && hmu_is_vo_freed(cur))
  82. || (ut == HMU_WO && !hmu_is_wo_marked(cur))) {
  83. /* merge previous free areas with current one */
  84. if (!last)
  85. last = cur;
  86. if (ut == HMU_WO) {
  87. /* Invoke registered finalizer */
  88. gc_object_t cur_obj = hmu_to_obj(cur);
  89. if (gct_vm_get_extra_info_flag(cur_obj)) {
  90. extra_info_node_t *node = gc_search_extra_info_node(
  91. (gc_handle_t)heap, cur_obj, NULL);
  92. bh_assert(node);
  93. node->finalizer(node->obj, node->data);
  94. gc_unset_finalizer((gc_handle_t)heap, cur_obj);
  95. }
  96. }
  97. }
  98. else {
  99. /* current block is still live */
  100. if (last) {
  101. tot_free += (gc_size_t)((char *)cur - (char *)last);
  102. gci_add_fc(heap, last, (gc_size_t)((char *)cur - (char *)last));
  103. hmu_mark_pinuse(last);
  104. last = NULL;
  105. }
  106. if (ut == HMU_WO) {
  107. /* unmark it */
  108. hmu_unmark_wo(cur);
  109. }
  110. }
  111. cur = (hmu_t *)((char *)cur + size);
  112. }
  113. bh_assert(cur == end);
  114. if (last) {
  115. tot_free += (gc_size_t)((char *)cur - (char *)last);
  116. gci_add_fc(heap, last, (gc_size_t)((char *)cur - (char *)last));
  117. hmu_mark_pinuse(last);
  118. }
  119. heap->total_free_size = tot_free;
  120. #if GC_STAT_DATA != 0
  121. heap->total_gc_count++;
  122. if ((heap->current_size - tot_free) > heap->highmark_size)
  123. heap->highmark_size = heap->current_size - tot_free;
  124. #endif
  125. gc_update_threshold(heap);
  126. }
  127. /**
  128. * Add a to-expand node to the to-expand list
  129. *
  130. * @param heap should be a valid instance heap
  131. * @param obj should be a valid wo inside @heap
  132. *
  133. * @return GC_ERROR if there is no more resource for marking,
  134. * GC_SUCCESS if success
  135. */
  136. static int
  137. add_wo_to_expand(gc_heap_t *heap, gc_object_t obj)
  138. {
  139. mark_node_t *mark_node = NULL, *new_node = NULL;
  140. hmu_t *hmu = NULL;
  141. bh_assert(obj);
  142. hmu = obj_to_hmu(obj);
  143. bh_assert(gci_is_heap_valid(heap));
  144. bh_assert((gc_uint8 *)hmu >= heap->base_addr
  145. && (gc_uint8 *)hmu < heap->base_addr + heap->current_size);
  146. bh_assert(hmu_get_ut(hmu) == HMU_WO);
  147. if (hmu_is_wo_marked(hmu))
  148. return GC_SUCCESS; /* already marked*/
  149. mark_node = (mark_node_t *)heap->root_set;
  150. if (!mark_node || mark_node->idx == mark_node->cnt) {
  151. new_node = alloc_mark_node();
  152. if (!new_node) {
  153. LOG_ERROR("can not add obj to mark node because of mark node "
  154. "allocation failed");
  155. return GC_ERROR;
  156. }
  157. new_node->next = mark_node;
  158. heap->root_set = new_node;
  159. mark_node = new_node;
  160. }
  161. mark_node->set[mark_node->idx++] = obj;
  162. hmu_mark_wo(hmu);
  163. return GC_SUCCESS;
  164. }
  165. /* Check ems_gc.h for description*/
  166. int
  167. gc_add_root(void *heap_p, gc_object_t obj)
  168. {
  169. gc_heap_t *heap = (gc_heap_t *)heap_p;
  170. hmu_t *hmu = NULL;
  171. if (!obj) {
  172. LOG_ERROR("gc_add_root with NULL obj");
  173. return GC_ERROR;
  174. }
  175. hmu = obj_to_hmu(obj);
  176. if (!gci_is_heap_valid(heap)) {
  177. LOG_ERROR("vm_get_gc_handle_for_current_instance returns invalid heap");
  178. return GC_ERROR;
  179. }
  180. if (!((gc_uint8 *)hmu >= heap->base_addr
  181. && (gc_uint8 *)hmu < heap->base_addr + heap->current_size)) {
  182. LOG_ERROR("Obj is not a object in current instance heap");
  183. return GC_ERROR;
  184. }
  185. if (hmu_get_ut(hmu) != HMU_WO) {
  186. LOG_ERROR("Given object is not wo");
  187. return GC_ERROR;
  188. }
  189. if (add_wo_to_expand(heap, obj) != GC_SUCCESS) {
  190. heap->is_fast_marking_failed = 1;
  191. return GC_ERROR;
  192. }
  193. return GC_SUCCESS;
  194. }
  195. /**
  196. * Unmark all marked objects to do rollback
  197. *
  198. * @param heap the heap to do rollback, should be a valid instance heap
  199. */
  200. static void
  201. rollback_mark(gc_heap_t *heap)
  202. {
  203. mark_node_t *mark_node = NULL, *next_mark_node = NULL;
  204. hmu_t *cur = NULL, *end = NULL;
  205. hmu_type_t ut;
  206. gc_size_t size;
  207. bh_assert(gci_is_heap_valid(heap));
  208. /* roll back*/
  209. mark_node = (mark_node_t *)heap->root_set;
  210. while (mark_node) {
  211. next_mark_node = mark_node->next;
  212. free_mark_node(mark_node);
  213. mark_node = next_mark_node;
  214. }
  215. heap->root_set = NULL;
  216. /* then traverse the heap to unmark all marked wos*/
  217. cur = (hmu_t *)heap->base_addr;
  218. end = (hmu_t *)((char *)heap->base_addr + heap->current_size);
  219. while (cur < end) {
  220. ut = hmu_get_ut(cur);
  221. size = hmu_get_size(cur);
  222. if (ut == HMU_WO && hmu_is_wo_marked(cur)) {
  223. hmu_unmark_wo(cur);
  224. }
  225. cur = (hmu_t *)((char *)cur + size);
  226. }
  227. bh_assert(cur == end);
  228. }
  229. /**
  230. * Reclaim GC instance heap
  231. *
  232. * @param heap the heap to reclaim, should be a valid instance heap
  233. *
  234. * @return GC_SUCCESS if success, GC_ERROR otherwise
  235. */
  236. static int
  237. reclaim_instance_heap(gc_heap_t *heap)
  238. {
  239. mark_node_t *mark_node = NULL;
  240. int idx = 0, j = 0;
  241. bool ret, is_compact_mode = false;
  242. gc_object_t obj = NULL, ref = NULL;
  243. hmu_t *hmu = NULL;
  244. gc_uint32 ref_num = 0, ref_start_offset = 0, size = 0, offset = 0;
  245. gc_uint16 *ref_list = NULL;
  246. bh_assert(gci_is_heap_valid(heap));
  247. heap->root_set = NULL;
  248. #if WASM_ENABLE_THREAD_MGR == 0
  249. if (!heap->exec_env)
  250. return GC_SUCCESS;
  251. ret = gct_vm_begin_rootset_enumeration(heap->exec_env, heap);
  252. #else
  253. if (!heap->cluster)
  254. return GC_SUCCESS;
  255. ret = gct_vm_begin_rootset_enumeration(heap->cluster, heap);
  256. #endif
  257. if (!ret) {
  258. if (heap->root_set) {
  259. rollback_mark(heap);
  260. }
  261. return GC_ERROR;
  262. }
  263. #if BH_ENABLE_GC_VERIFY != 0
  264. /* no matter whether the enumeration is successful or not, the data
  265. collected should be checked at first */
  266. mark_node = (mark_node_t *)heap->root_set;
  267. while (mark_node) {
  268. /* all nodes except first should be full filled */
  269. bh_assert(mark_node == (mark_node_t *)heap->root_set
  270. || mark_node->idx == mark_node->cnt);
  271. /* all nodes should be non-empty */
  272. bh_assert(mark_node->idx > 0);
  273. for (idx = 0; idx < (int)mark_node->idx; idx++) {
  274. obj = mark_node->set[idx];
  275. hmu = obj_to_hmu(obj);
  276. bh_assert(hmu_is_wo_marked(hmu));
  277. bh_assert((gc_uint8 *)hmu >= heap->base_addr
  278. && (gc_uint8 *)hmu
  279. < heap->base_addr + heap->current_size);
  280. }
  281. mark_node = mark_node->next;
  282. }
  283. #endif
  284. /* TODO: when fast marking failed, we can still do slow
  285. marking, currently just simply roll it back. */
  286. if (heap->is_fast_marking_failed) {
  287. LOG_ERROR("enumerate rootset failed");
  288. LOG_ERROR("all marked wos will be unmarked to keep heap consistency");
  289. rollback_mark(heap);
  290. heap->is_fast_marking_failed = 0;
  291. return GC_ERROR;
  292. }
  293. /* the algorithm we use to mark all objects */
  294. /* 1. mark rootset and organize them into a mark_node list (last marked
  295. * roots at list header, i.e. stack top) */
  296. /* 2. in every iteration, we use the top node to expand*/
  297. /* 3. execute step 2 till no expanding */
  298. /* this is a BFS & DFS mixed algorithm, but more like DFS */
  299. mark_node = (mark_node_t *)heap->root_set;
  300. while (mark_node) {
  301. heap->root_set = mark_node->next;
  302. /* note that mark_node->idx may change in each loop */
  303. for (idx = 0; idx < (int)mark_node->idx; idx++) {
  304. obj = mark_node->set[idx];
  305. hmu = obj_to_hmu(obj);
  306. size = hmu_get_size(hmu);
  307. if (!gct_vm_get_wasm_object_ref_list(obj, &is_compact_mode,
  308. &ref_num, &ref_list,
  309. &ref_start_offset)) {
  310. LOG_ERROR("mark process failed because failed "
  311. "vm_get_wasm_object_ref_list");
  312. break;
  313. }
  314. if (ref_num >= 2U * GB) {
  315. LOG_ERROR("Invalid ref_num returned");
  316. break;
  317. }
  318. if (is_compact_mode) {
  319. for (j = 0; j < (int)ref_num; j++) {
  320. offset = ref_start_offset + j * sizeof(void *);
  321. bh_assert(offset + sizeof(void *) < size);
  322. ref = *(gc_object_t *)(((gc_uint8 *)obj) + offset);
  323. if (ref == NULL_REF || ((uintptr_t)ref & 1))
  324. continue; /* null object or i31 object */
  325. if (add_wo_to_expand(heap, ref) == GC_ERROR) {
  326. LOG_ERROR("add_wo_to_expand failed");
  327. break;
  328. }
  329. }
  330. if (j < (int)ref_num)
  331. break;
  332. }
  333. else {
  334. for (j = 0; j < (int)ref_num; j++) {
  335. offset = ref_list[j];
  336. bh_assert(offset + sizeof(void *) < size);
  337. ref = *(gc_object_t *)(((gc_uint8 *)obj) + offset);
  338. if (ref == NULL_REF || ((uintptr_t)ref & 1))
  339. continue; /* null object or i31 object */
  340. if (add_wo_to_expand(heap, ref) == GC_ERROR) {
  341. LOG_ERROR("mark process failed");
  342. break;
  343. }
  344. }
  345. if (j < (int)ref_num)
  346. break;
  347. }
  348. }
  349. if (idx < (int)mark_node->idx)
  350. break; /* not yet done */
  351. /* obj's in mark_node are all expanded */
  352. free_mark_node(mark_node);
  353. mark_node = heap->root_set;
  354. }
  355. if (mark_node) {
  356. LOG_ERROR("mark process is not successfully finished");
  357. free_mark_node(mark_node);
  358. /* roll back is required */
  359. rollback_mark(heap);
  360. return GC_ERROR;
  361. }
  362. /* now sweep */
  363. sweep_instance_heap(heap);
  364. (void)size;
  365. return GC_SUCCESS;
  366. }
  367. /**
  368. * Do GC on given heap
  369. *
  370. * @param the heap to do GC, should be a valid heap
  371. *
  372. * @return GC_SUCCESS if success, GC_ERROR otherwise
  373. */
  374. int
  375. gci_gc_heap(void *h)
  376. {
  377. int ret = GC_ERROR;
  378. gc_heap_t *heap = (gc_heap_t *)h;
  379. bh_assert(gci_is_heap_valid(heap));
  380. LOG_VERBOSE("#reclaim instance heap %p", heap);
  381. /* TODO: get exec_env of current thread when GC multi-threading
  382. is enabled, and pass it to runtime */
  383. gct_vm_gc_prepare(NULL);
  384. gct_vm_mutex_lock(&heap->lock);
  385. heap->is_doing_reclaim = 1;
  386. ret = reclaim_instance_heap(heap);
  387. heap->is_doing_reclaim = 0;
  388. gct_vm_mutex_unlock(&heap->lock);
  389. /* TODO: get exec_env of current thread when GC multi-threading
  390. is enabled, and pass it to runtime */
  391. gct_vm_gc_finished(NULL);
  392. LOG_VERBOSE("#reclaim instance heap %p done", heap);
  393. #if BH_ENABLE_GC_VERIFY != 0
  394. gci_verify_heap(heap);
  395. #endif
  396. #if GC_STAT_SHOW != 0
  397. gc_show_stat(heap);
  398. gc_show_fragment(heap);
  399. #endif
  400. return ret;
  401. }
  402. int
  403. gc_is_dead_object(void *obj)
  404. {
  405. return !hmu_is_wo_marked(obj_to_hmu(obj));
  406. }
  407. #else
  408. int
  409. gci_gc_heap(void *h)
  410. {
  411. (void)h;
  412. return GC_ERROR;
  413. }
  414. #endif /* end of WASM_ENABLE_GC != 0 */