/* Some objects, having been copied, need no further attention. Others * need to do some additional freeing, however. This goes through the * fromspace and does any needed work to free uncopied things (this may * run in parallel with the mutator, which will be operating on tospace). */ void MVM_gc_collect_free_nursery_uncopied(MVMThreadContext *tc, void *limit) { /* We start scanning the fromspace, and keep going until we hit * the end of the area allocated in it. */ void *scan = tc->nursery_fromspace; while (scan < limit) { /* The object here is dead if it never got a forwarding pointer * written in to it. */ MVMCollectable *item = (MVMCollectable *)scan; MVMuint8 dead = item->forwarder == NULL; /* Now go by collectable type. */ if (!(item->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { /* Object instance. If dead, call gc_free if needed. Scan is * incremented by object size. */ MVMObject *obj = (MVMObject *)item; GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : collecting an object %p in the nursery with reprid %d\n", item, REPR(obj)->ID); if (dead && REPR(obj)->gc_free) REPR(obj)->gc_free(tc, obj); } else if (item->flags & MVM_CF_TYPE_OBJECT) { /* Type object; doesn't have anything extra that needs freeing. */ } else if (item->flags & MVM_CF_STABLE) { MVMSTable *st = (MVMSTable *)item; if (dead) { /* GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : enqueuing an STable %d in the nursery to be freed\n", item);*/ MVM_gc_collect_enqueue_stable_for_deletion(tc, st); } } else { printf("item flags: %d\n", item->flags); MVM_panic(MVM_exitcode_gcnursery, "Internal error: impossible case encountered in GC free"); } /* Go to the next item. */ scan = (char *)scan + item->size; } }
/* Goes through the unmarked objects in the second generation heap and builds * free lists out of them. Also does any required finalization. */ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc, MVMint32 global_destruction) { /* Visit each of the size class bins. */ MVMGen2Allocator *gen2 = tc->gen2; MVMuint32 bin, obj_size, page, i; char ***freelist_insert_pos; for (bin = 0; bin < MVM_GEN2_BINS; bin++) { /* If we've nothing allocated in this size class, skip it. */ if (gen2->size_classes[bin].pages == NULL) continue; /* Calculate object size for this bin. */ obj_size = (bin + 1) << MVM_GEN2_BIN_BITS; /* freelist_insert_pos is a pointer to a memory location that * stores the address of the last traversed free list node (char **). */ /* Initialize freelist insertion position to free list head. */ freelist_insert_pos = &gen2->size_classes[bin].free_list; /* Visit each page. */ for (page = 0; page < gen2->size_classes[bin].num_pages; page++) { /* Visit all the objects, looking for dead ones and reset the * mark for each of them. */ char *cur_ptr = gen2->size_classes[bin].pages[page]; char *end_ptr = page + 1 == gen2->size_classes[bin].num_pages ? gen2->size_classes[bin].alloc_pos : cur_ptr + obj_size * MVM_GEN2_PAGE_ITEMS; while (cur_ptr < end_ptr) { MVMCollectable *col = (MVMCollectable *)cur_ptr; /* Is this already a free list slot? If so, it becomes the * new free list insert position. */ if (*freelist_insert_pos == (char **)cur_ptr) { freelist_insert_pos = (char ***)cur_ptr; } /* Otherwise, it must be a collectable of some kind. Is it * live? */ else if (col->flags & MVM_CF_GEN2_LIVE) { /* Yes; clear the mark. */ col->flags &= ~MVM_CF_GEN2_LIVE; } else { GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : collecting an object %p in the gen2\n", col); /* No, it's dead. Do any cleanup. */ if (!(col->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { /* Object instance; call gc_free if needed. */ MVMObject *obj = (MVMObject *)col; if (STABLE(obj) && REPR(obj)->gc_free) REPR(obj)->gc_free(tc, obj); #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX if (col->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) MVM_free(col->sc_forward_u.sci); #endif } else if (col->flags & MVM_CF_TYPE_OBJECT) { #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX if (col->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) MVM_free(col->sc_forward_u.sci); #endif } else if (col->flags & MVM_CF_STABLE) { if ( #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX !(col->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) && #endif col->sc_forward_u.sc.sc_idx == 0 && col->sc_forward_u.sc.idx == MVM_DIRECT_SC_IDX_SENTINEL) { /* We marked it dead last time, kill it. */ MVM_6model_stable_gc_free(tc, (MVMSTable *)col); } else { #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX if (col->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) { /* Whatever happens next, we can free this memory immediately, because no-one will be serializing a dead STable. */ assert(!(col->sc_forward_u.sci->sc_idx == 0 && col->sc_forward_u.sci->idx == MVM_DIRECT_SC_IDX_SENTINEL)); MVM_free(col->sc_forward_u.sci); col->flags &= ~MVM_CF_SERIALZATION_INDEX_ALLOCATED; } #endif if (global_destruction) { /* We're in global destruction, so enqueue to the end * like we do in the nursery */ MVM_gc_collect_enqueue_stable_for_deletion(tc, (MVMSTable *)col); } else { /* There will definitely be another gc run, so mark it as "died last time". */ col->sc_forward_u.sc.sc_idx = 0; col->sc_forward_u.sc.idx = MVM_DIRECT_SC_IDX_SENTINEL; } /* Skip the freelist updating. */ cur_ptr += obj_size; continue; } } else { printf("item flags: %d\n", col->flags); MVM_panic(MVM_exitcode_gcnursery, "Internal error: impossible case encountered in gen2 GC free"); } /* Chain in to the free list. */ *((char **)cur_ptr) = (char *)*freelist_insert_pos; *freelist_insert_pos = (char **)cur_ptr; /* Update the pointer to the insert position to point to us */ freelist_insert_pos = (char ***)cur_ptr; } /* Move to the next object. */ cur_ptr += obj_size; } } } /* Also need to consider overflows. */ for (i = 0; i < gen2->num_overflows; i++) { if (gen2->overflows[i]) { MVMCollectable *col = gen2->overflows[i]; if (col->flags & MVM_CF_GEN2_LIVE) { /* A living over-sized object; just clear the mark. */ col->flags &= ~MVM_CF_GEN2_LIVE; } else { /* Dead over-sized object. We know if it's this big it cannot * be a type object or STable, so only need handle the simple * object case. */ if (!(col->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { MVMObject *obj = (MVMObject *)col; if (REPR(obj)->gc_free) REPR(obj)->gc_free(tc, obj); #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX if (col->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) MVM_free(col->sc_forward_u.sci); #endif } else { MVM_panic(MVM_exitcode_gcnursery, "Internal error: gen2 overflow contains non-object"); } MVM_free(col); gen2->overflows[i] = NULL; } } } /* And finally compact the overflow list */ MVM_gc_gen2_compact_overflows(gen2); }
/* Some objects, having been copied, need no further attention. Others * need to do some additional freeing, however. This goes through the * fromspace and does any needed work to free uncopied things (this may * run in parallel with the mutator, which will be operating on tospace). */ void MVM_gc_collect_free_nursery_uncopied(MVMThreadContext *tc, void *limit) { /* We start scanning the fromspace, and keep going until we hit * the end of the area allocated in it. */ void *scan = tc->nursery_fromspace; while (scan < limit) { /* The object here is dead if it never got a forwarding pointer * written in to it. */ MVMCollectable *item = (MVMCollectable *)scan; MVMuint8 dead = !(item->flags & MVM_CF_FORWARDER_VALID); if (!dead) assert(item->sc_forward_u.forwarder != NULL); /* Now go by collectable type. */ if (!(item->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { /* Object instance. If dead, call gc_free if needed. Scan is * incremented by object size. */ MVMObject *obj = (MVMObject *)item; GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : collecting an object %p in the nursery with reprid %d\n", item, REPR(obj)->ID); if (dead && REPR(obj)->gc_free) REPR(obj)->gc_free(tc, obj); #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX if (dead && item->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) MVM_free(item->sc_forward_u.sci); #endif if (dead && item->flags & MVM_CF_HAS_OBJECT_ID) MVM_gc_object_id_clear(tc, item); } else if (item->flags & MVM_CF_TYPE_OBJECT) { /* Type object */ #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX if (dead && item->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) MVM_free(item->sc_forward_u.sci); #endif if (dead && item->flags & MVM_CF_HAS_OBJECT_ID) MVM_gc_object_id_clear(tc, item); } else if (item->flags & MVM_CF_STABLE) { MVMSTable *st = (MVMSTable *)item; if (dead) { /* GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : enqueuing an STable %d in the nursery to be freed\n", item);*/ #ifdef MVM_USE_OVERFLOW_SERIALIZATION_INDEX if (item->flags & MVM_CF_SERIALZATION_INDEX_ALLOCATED) { MVM_free(item->sc_forward_u.sci); /* Arguably we don't need to do this, if we're always consistent about what we put on the stable queue. */ item->flags &= ~MVM_CF_SERIALZATION_INDEX_ALLOCATED; } #endif MVM_gc_collect_enqueue_stable_for_deletion(tc, st); } } else { printf("item flags: %d\n", item->flags); MVM_panic(MVM_exitcode_gcnursery, "Internal error: impossible case encountered in GC free"); } /* Go to the next item. */ scan = (char *)scan + item->size; } }
/* Goes through the unmarked objects in the second generation heap and builds * free lists out of them. Also does any required finalization. */ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc) { /* Visit each of the size class bins. */ MVMGen2Allocator *gen2 = tc->gen2; MVMuint32 bin, obj_size, page, i; char ***freelist_insert_pos; for (bin = 0; bin < MVM_GEN2_BINS; bin++) { /* If we've nothing allocated in this size class, skip it. */ if (gen2->size_classes[bin].pages == NULL) continue; /* Calculate object size for this bin. */ obj_size = (bin + 1) << MVM_GEN2_BIN_BITS; /* freelist_insert_pos is a pointer to a memory location that * stores the address of the last traversed free list node (char **). */ /* Initialize freelist insertion position to free list head. */ freelist_insert_pos = &gen2->size_classes[bin].free_list; /* Visit each page. */ for (page = 0; page < gen2->size_classes[bin].num_pages; page++) { /* Visit all the objects, looking for dead ones and reset the * mark for each of them. */ char *cur_ptr = gen2->size_classes[bin].pages[page]; char *end_ptr = page + 1 == gen2->size_classes[bin].num_pages ? gen2->size_classes[bin].alloc_pos : cur_ptr + obj_size * MVM_GEN2_PAGE_ITEMS; char **last_insert_pos = NULL; while (cur_ptr < end_ptr) { MVMCollectable *col = (MVMCollectable *)cur_ptr; /* Is this already a free list slot? If so, it becomes the * new free list insert position. */ if (*freelist_insert_pos == (char **)cur_ptr) { freelist_insert_pos = (char ***)cur_ptr; } /* Otherwise, it must be a collectable of some kind. Is it * live? */ else if (col->forwarder) { /* Yes; clear the mark. */ col->forwarder = NULL; } else { GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : collecting an object %p in the gen2\n", col); /* No, it's dead. Do any cleanup. */ if (!(col->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { /* Object instance; call gc_free if needed. */ MVMObject *obj = (MVMObject *)col; if (REPR(obj)->gc_free) REPR(obj)->gc_free(tc, obj); } else if (col->flags & MVM_CF_TYPE_OBJECT) { /* Type object; doesn't have anything extra that needs freeing. */ } else if (col->flags & MVM_CF_STABLE) { if (col->sc == (MVMSerializationContext *)1) { /* We marked it dead last time, kill it. */ MVM_6model_stable_gc_free(tc, (MVMSTable *)col); } else { if (MVM_load(&tc->gc_status) == MVMGCStatus_NONE) { /* We're in global destruction, so enqueue to the end * like we do in the nursery */ MVM_gc_collect_enqueue_stable_for_deletion(tc, (MVMSTable *)col); } else { /* There will definitely be another gc run, so mark it as "died last time". */ col->sc = (MVMSerializationContext *)1; } /* Skip the freelist updating. */ cur_ptr += obj_size; continue; } } else { printf("item flags: %d\n", col->flags); MVM_panic(MVM_exitcode_gcnursery, "Internal error: impossible case encountered in gen2 GC free"); } /* Chain in to the free list. */ *((char **)cur_ptr) = (char *)*freelist_insert_pos; *freelist_insert_pos = (char **)cur_ptr; /* Update the pointer to the insert position to point to us */ freelist_insert_pos = (char ***)cur_ptr; } /* Move to the next object. */ cur_ptr += obj_size; } } } /* Also need to consider overflows. */ for (i = 0; i < gen2->num_overflows; i++) { if (gen2->overflows[i]) { MVMCollectable *col = gen2->overflows[i]; if (col->forwarder) { /* A living over-sized object; just clear the mark. */ col->forwarder = NULL; } else { /* Dead over-sized object. We know if it's this big it cannot * be a type object or STable, so only need handle the simple * object case. */ if (!(col->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { MVMObject *obj = (MVMObject *)col; if (REPR(obj)->gc_free) REPR(obj)->gc_free(tc, obj); } else { MVM_panic(MVM_exitcode_gcnursery, "Internal error: gen2 overflow contains non-object"); } free(col); gen2->overflows[i] = NULL; } } } }