Attached is a rough cut at shrinking the GC header. It's tidier than the working version, but it still has a lot of assert()s in it, which aren't (yet) removed by the C pre-processor under --optimize [However, none fail]
I'm also not very happy with adding a couple of non-inline functions: commit b575e123dfc6ab49639fec28871247b8fe8bdeab Author: Nicholas Clark <n...@ccl4.org> Date: Wed Jan 29 21:41:26 2014 +0100 Encapsulate SC access through MVM_sc_{get,set}_{stable,obj}_sc() Ideally these would be inline functions. diff --git a/src/6model/sc.c b/src/6model/sc.c index 3114bbd..93dc8a5 100644 --- a/src/6model/sc.c +++ b/src/6model/sc.c @@ -255,6 +255,16 @@ MVMuint64 MVM_sc_get_object_count(MVMThreadContext *tc, MVMSerializationContext return sc->body->num_objects; } +/* Gets an object's SC. */ +MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj) { + return obj->header.sc; +} + +/* Gets an STables's SC. */ +MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable *st) { + return st->header.sc; +} + /* Sets an object's SC. */ void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationContext *sc) { MVM_ASSIGN_REF(tc, obj, obj->header.sc, sc); [snip] diff --git a/src/6model/sc.h b/src/6model/sc.h index 7edc67d..e449d17 100644 --- a/src/6model/sc.h +++ b/src/6model/sc.h @@ -18,6 +18,8 @@ MVMObject * MVM_sc_get_code(MVMThreadContext *tc, MVMSerializationContext *sc, M void MVM_sc_set_code(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx, MVMObject *code); void MVM_sc_set_code_list(MVMThreadContext *tc, MVMSerializationContext *sc, MVMObject *code_list); MVMuint64 MVM_sc_get_object_count(MVMThreadContext *tc, MVMSerializationContext *sc); +MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj); +MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable *st); void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationContext *sc); void MVM_sc_set_stable_sc(MVMThreadContext *tc, MVMSTable *st, MVMSerializationContext *sc); MVMSerializationContext * MVM_sc_find_by_handle(MVMThreadContext *tc, MVMString *handle); but for now I needed them to hang the assertions off that prove that I'm not doing anything (*too*) stupid. Preliminary results [ie 1 run, on a shared machine :-)] suggests that it reduces RSS size for compiling the setting by 4.8% (roughly what I expected) and seemingly CPU time by 11% (more than I expected, but I guess all those L2 cache misses now saved were costing us) Not tested on any 32 bit platform yet. Not sure what to do next, but currently I'm somewhat sleep deprived after a long night, to the point of not even being reliably able to operate a kettle, so there may be errors in this. Nicholas Clark
>From f713391e5e43a1761c206cabcf0ba8fa92d4b82a Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Tue, 28 Jan 2014 21:05:51 +0100 Subject: [PATCH 01/10] In process_worklist(), assert() that various assignments are unneeded. Also assert that if the pointer is in to-space already, it is never marked as gen2. --- src/gc/collect.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/gc/collect.c b/src/gc/collect.c index a88e985..890689d 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -176,7 +176,8 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work /* If the item was already seen and copied, then it will have a * forwarding address already. Just update this pointer to the * new address and we're done. */ - if (item->forwarder) { + if (!item_gen2 && item->forwarder) { + assert(*item_ptr != item->forwarder); if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) { if (*item_ptr != item->forwarder) { GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : updating handle %p from %p to forwarder %p\n", item_ptr, item, item->forwarder); @@ -187,14 +188,20 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work } *item_ptr = item->forwarder; continue; + } else if (item->forwarder) { + /* gen2 and marked as live. */ + assert(*item_ptr == item->forwarder); + continue; } /* If the pointer is already into tospace (the bit we've already copied * into), we already updated it, so we're done. If it's in to-space but * *ahead* of our copy offset then it's an out-of-date pointer and we * have some kind of corruption. */ - if (item >= (MVMCollectable *)tc->nursery_tospace && item < (MVMCollectable *)tc->nursery_alloc) + if (item >= (MVMCollectable *)tc->nursery_tospace && item < (MVMCollectable *)tc->nursery_alloc) { + assert(!item_gen2); continue; + } if (item >= (MVMCollectable *)tc->nursery_alloc && item < (MVMCollectable *)tc->nursery_alloc_limit) MVM_panic(1, "Heap corruption detected: pointer %p to past fromspace", item); @@ -216,7 +223,8 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) { GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : handle %p was already %p\n", item_ptr, new_addr); } - *item_ptr = item->forwarder = new_addr; + item->forwarder = new_addr; + assert(*item_ptr == new_addr); } else { /* Catch NULL stable (always sign of trouble) in debug mode. */ if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT) && !STABLE(item)) { -- 1.8.4.2
>From 1fd2462dc95d34d4e25953ba30a5d0634f309f39 Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Tue, 28 Jan 2014 21:27:22 +0100 Subject: [PATCH 02/10] Re-order code in process_worklist() to avoid checking tospace for gen2 items. Still run the sanity test for tospace even on items marked as gen2. --- src/gc/collect.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/gc/collect.c b/src/gc/collect.c index 890689d..9ba07dd 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -170,13 +170,18 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work /* If it's in the second generation and we're only doing a nursery, * collection, we have nothing to do. */ item_gen2 = item->flags & MVM_CF_SECOND_GEN; - if (item_gen2 && gen == MVMGCGenerations_Nursery) - continue; - - /* If the item was already seen and copied, then it will have a - * forwarding address already. Just update this pointer to the - * new address and we're done. */ - if (!item_gen2 && item->forwarder) { + if (item_gen2) { + if (gen == MVMGCGenerations_Nursery) + continue; + if (item->forwarder) { + /* gen2 and marked as live. */ + assert(*item_ptr == item->forwarder); + continue; + } + } else if (item->forwarder) { + /* If the item was already seen and copied, then it will have a + * forwarding address already. Just update this pointer to the + * new address and we're done. */ assert(*item_ptr != item->forwarder); if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) { if (*item_ptr != item->forwarder) { @@ -188,20 +193,16 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work } *item_ptr = item->forwarder; continue; - } else if (item->forwarder) { - /* gen2 and marked as live. */ - assert(*item_ptr == item->forwarder); - continue; + } else { + /* If the pointer is already into tospace (the bit we've already + copied into), we already updated it, so we're done. */ + if (item >= (MVMCollectable *)tc->nursery_tospace && item < (MVMCollectable *)tc->nursery_alloc) { + continue; + } } - /* If the pointer is already into tospace (the bit we've already copied - * into), we already updated it, so we're done. If it's in to-space but - * *ahead* of our copy offset then it's an out-of-date pointer and we - * have some kind of corruption. */ - if (item >= (MVMCollectable *)tc->nursery_tospace && item < (MVMCollectable *)tc->nursery_alloc) { - assert(!item_gen2); - continue; - } + /* If it's in to-space but *ahead* of our copy offset then it's an + out-of-date pointer and we have some kind of corruption. */ if (item >= (MVMCollectable *)tc->nursery_alloc && item < (MVMCollectable *)tc->nursery_alloc_limit) MVM_panic(1, "Heap corruption detected: pointer %p to past fromspace", item); -- 1.8.4.2
>From b402353dc8713fa6822bcb9f97691de789c85705 Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Wed, 29 Jan 2014 00:09:45 +0100 Subject: [PATCH 03/10] Mark gen2 as live using a flag bit instead of the forwarding pointer. This means that the forwarding pointer is now only used on dead objects. --- src/6model/6model.h | 5 ++++- src/gc/collect.c | 22 ++++++++++------------ src/gc/roots.c | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/6model/6model.h b/src/6model/6model.h index 5484a05..ff2a461 100644 --- a/src/6model/6model.h +++ b/src/6model/6model.h @@ -99,7 +99,10 @@ typedef enum { /* Has already been added to the gen2 aggregates pointing to nursery * objects list. */ - MVM_CF_IN_GEN2_ROOT_LIST = 32 + MVM_CF_IN_GEN2_ROOT_LIST = 32, + + /* GC has found this object to be live. */ + MVM_CF_SECOND_GEN_LIVE = 64 } MVMCollectableFlags; /* Things that every GC-collectable entity has. These fall into two diff --git a/src/gc/collect.c b/src/gc/collect.c index 9ba07dd..c0a7f57 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -173,9 +173,8 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work if (item_gen2) { if (gen == MVMGCGenerations_Nursery) continue; - if (item->forwarder) { + if (item->flags & MVM_CF_SECOND_GEN_LIVE) { /* gen2 and marked as live. */ - assert(*item_ptr == item->forwarder); continue; } } else if (item->forwarder) { @@ -217,14 +216,13 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work /* At this point, we didn't already see the object, which means we * need to take some action. Go on the generation... */ if (item_gen2) { - /* It's in the second generation. We'll just mark it, which is - * done by setting the forwarding pointer to the object itself, - * since we don't do moving. */ + assert(!item->forwarder); + /* It's in the second generation. We'll just mark it. */ new_addr = item; if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) { GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : handle %p was already %p\n", item_ptr, new_addr); } - item->forwarder = new_addr; + item->flags |= MVM_CF_SECOND_GEN_LIVE; assert(*item_ptr == new_addr); } else { /* Catch NULL stable (always sign of trouble) in debug mode. */ @@ -259,7 +257,7 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work /* If we're going to sweep the second generation, also need * to mark it as live. */ if (gen == MVMGCGenerations_Both) - new_addr->forwarder = new_addr; + new_addr->flags |= MVM_CF_SECOND_GEN_LIVE; } else { /* No, so it will live in the nursery for another GC @@ -554,7 +552,7 @@ void MVM_gc_collect_cleanup_gen2roots(MVMThreadContext *tc) { MVMuint32 ins_pos = 0; MVMuint32 i; for (i = 0; i < num_roots; i++) - if (gen2roots[i]->forwarder) + if (gen2roots[i]->flags & MVM_CF_SECOND_GEN_LIVE) gen2roots[ins_pos++] = gen2roots[i]; tc->num_gen2roots = ins_pos; } @@ -611,9 +609,9 @@ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc) { /* Otherwise, it must be a collectable of some kind. Is it * live? */ - else if (col->forwarder) { + else if (col->flags & MVM_CF_SECOND_GEN_LIVE) { /* Yes; clear the mark. */ - col->forwarder = NULL; + col->flags &= ~MVM_CF_SECOND_GEN_LIVE; } else { GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : collecting an object %p in the gen2\n", col); @@ -669,9 +667,9 @@ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc) { for (i = 0; i < gen2->num_overflows; i++) { if (gen2->overflows[i]) { MVMCollectable *col = gen2->overflows[i]; - if (col->forwarder) { + if (col->flags & MVM_CF_SECOND_GEN_LIVE) { /* A living over-sized object; just clear the mark. */ - col->forwarder = NULL; + col->flags &= ~MVM_CF_SECOND_GEN_LIVE; } else { /* Dead over-sized object. We know if it's this big it cannot diff --git a/src/gc/roots.c b/src/gc/roots.c index 1e7c395..17a2921 100644 --- a/src/gc/roots.c +++ b/src/gc/roots.c @@ -239,8 +239,8 @@ void MVM_gc_root_gen2_cleanup(MVMThreadContext *tc) { MVMuint32 cur_survivor = 0; MVMuint32 i; for (i = 0; i < num_roots; i++) - if (gen2roots[i]->forwarder) - gen2roots[cur_survivor++] = gen2roots[i]->forwarder; + if (gen2roots[i]->flags & MVM_CF_SECOND_GEN_LIVE) + gen2roots[cur_survivor++] = gen2roots[i]; tc->num_gen2roots = cur_survivor; } -- 1.8.4.2
>From ea69e8d6d47523e606a0512a6513841f92d9ab0b Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Wed, 29 Jan 2014 08:33:54 +0100 Subject: [PATCH 04/10] Use a flag to indicate which items in fromspace are live (during GC). --- src/6model/6model.h | 6 +++++- src/gc/collect.c | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/6model/6model.h b/src/6model/6model.h index ff2a461..0d7084e 100644 --- a/src/6model/6model.h +++ b/src/6model/6model.h @@ -102,7 +102,11 @@ typedef enum { MVM_CF_IN_GEN2_ROOT_LIST = 32, /* GC has found this object to be live. */ - MVM_CF_SECOND_GEN_LIVE = 64 + MVM_CF_SECOND_GEN_LIVE = 64, + /* This object in fromspace is live with a valid forwarder. */ + /* TODO - should be possible to use the same bit for these two flags. */ + MVM_CF_FORWARDER_VALID = 128 + } MVMCollectableFlags; /* Things that every GC-collectable entity has. These fall into two diff --git a/src/gc/collect.c b/src/gc/collect.c index c0a7f57..e39dedb 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -177,7 +177,7 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work /* gen2 and marked as live. */ continue; } - } else if (item->forwarder) { + } else if (item->flags & MVM_CF_FORWARDER_VALID) { /* If the item was already seen and copied, then it will have a * forwarding address already. Just update this pointer to the * new address and we're done. */ @@ -280,6 +280,9 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : updating handle %p from referent %p (reprid %d) to %p\n", item_ptr, item, REPR(item)->ID, new_addr); } *item_ptr = item->forwarder = new_addr; + /* Set the flag on the copy of item *in fromspace* to mark that the + forwarder pointer is valid. */ + item->flags |= MVM_CF_FORWARDER_VALID; } /* make sure any rooted frames mark their stuff */ @@ -512,7 +515,12 @@ void MVM_gc_collect_free_nursery_uncopied(MVMThreadContext *tc, void *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; + MVMuint8 dead = !(item->flags & MVM_CF_FORWARDER_VALID); + + if (!dead) + assert(item->forwarder != NULL); + else + assert(item->forwarder == NULL); /* Now go by collectable type. */ if (!(item->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { -- 1.8.4.2
>From c3dc5a2ff4b05beae59ff90524ac8696883ab5e4 Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Wed, 29 Jan 2014 12:59:15 +0100 Subject: [PATCH 05/10] Some GC marking sanity check asserts. --- src/gc/collect.c | 3 +++ src/gc/roots.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/gc/collect.c b/src/gc/collect.c index e39dedb..1b6c9b2 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -317,6 +317,9 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work void MVM_gc_mark_collectable(MVMThreadContext *tc, MVMGCWorklist *worklist, MVMCollectable *new_addr) { MVMuint16 i; + assert(!new_addr->forwarder); + assert(!(new_addr->flags & MVM_CF_FORWARDER_VALID)); + assert(REPR(new_addr)); MVM_gc_worklist_add(tc, worklist, &new_addr->sc); if (!(new_addr->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { diff --git a/src/gc/roots.c b/src/gc/roots.c index 17a2921..aa65cad 100644 --- a/src/gc/roots.c +++ b/src/gc/roots.c @@ -140,6 +140,7 @@ void MVM_gc_root_gen2_add(MVMThreadContext *tc, MVMCollectable *c) { /* Ensure the collectable is not null. */ if (c == NULL) MVM_panic(MVM_exitcode_gcroots, "Illegal attempt to add null collectable address as an inter-generational root"); + assert(!(c->flags & MVM_CF_FORWARDER_VALID)); /* Allocate extra gen2 aggregate space if needed. */ if (tc->num_gen2roots == tc->alloc_gen2roots) { @@ -188,6 +189,8 @@ void MVM_gc_root_add_gen2s_to_worklist(MVMThreadContext *tc, MVMGCWorklist *work for (i = 0; i < num_roots; i++) { int num_in_nursery = 0; + assert(!(gen2roots[i]->flags & MVM_CF_FORWARDER_VALID)); + /* Mark it, putting marks into temporary worklist. */ MVM_gc_mark_collectable(tc, per_obj_worklist, gen2roots[i]); -- 1.8.4.2
>From b575e123dfc6ab49639fec28871247b8fe8bdeab Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Wed, 29 Jan 2014 21:41:26 +0100 Subject: [PATCH 06/10] Encapsulate SC access through MVM_sc_{get,set}_{stable,obj}_sc() Ideally these would be inline functions. --- src/6model/sc.c | 26 ++++++++++++++++-------- src/6model/sc.h | 2 ++ src/6model/serialization.c | 50 ++++++++++++++++++++++------------------------ src/core/interp.c | 11 +++++----- 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/6model/sc.c b/src/6model/sc.c index 3114bbd..93dc8a5 100644 --- a/src/6model/sc.c +++ b/src/6model/sc.c @@ -255,6 +255,16 @@ MVMuint64 MVM_sc_get_object_count(MVMThreadContext *tc, MVMSerializationContext return sc->body->num_objects; } +/* Gets an object's SC. */ +MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj) { + return obj->header.sc; +} + +/* Gets an STables's SC. */ +MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable *st) { + return st->header.sc; +} + /* Sets an object's SC. */ void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationContext *sc) { MVM_ASSIGN_REF(tc, obj, obj->header.sc, sc); @@ -288,7 +298,7 @@ void MVM_sc_wb_hit_obj(MVMThreadContext *tc, MVMObject *obj) { /* Otherwise, check that the object's SC is different from the SC * of the compilation we're currently in. Repossess if so. */ comp_sc = (MVMSerializationContext *)MVM_repr_at_pos_o(tc, tc->compiling_scs, 0); - if (obj->header.sc != comp_sc) { + if (MVM_sc_get_obj_sc(tc, obj) != comp_sc) { /* Get new slot ID. */ MVMint64 new_slot = comp_sc->body->num_objects; @@ -296,14 +306,14 @@ void MVM_sc_wb_hit_obj(MVMThreadContext *tc, MVMObject *obj) { * owner we need to repossess. */ if (obj->st->WHAT == tc->instance->boot_types.BOOTArray || obj->st->WHAT == tc->instance->boot_types.BOOTHash) { - MVMObject *owned_objects = obj->header.sc->body->owned_objects; + MVMObject *owned_objects = MVM_sc_get_obj_sc(tc, obj)->body->owned_objects; MVMint64 n = MVM_repr_elems(tc, owned_objects); MVMint64 found = 0; MVMint64 i; for (i = 0; i < n; i += 2) { if (MVM_repr_at_pos_o(tc, owned_objects, i) == obj) { obj = MVM_repr_at_pos_o(tc, owned_objects, i + 1); - if (obj->header.sc == comp_sc) + if (MVM_sc_get_obj_sc(tc, obj) == comp_sc) return; found = 1; break; @@ -318,10 +328,10 @@ void MVM_sc_wb_hit_obj(MVMThreadContext *tc, MVMObject *obj) { /* Add repossession entry. */ MVM_repr_push_i(tc, comp_sc->body->rep_indexes, new_slot << 1); - MVM_repr_push_o(tc, comp_sc->body->rep_scs, (MVMObject *)obj->header.sc); + MVM_repr_push_o(tc, comp_sc->body->rep_scs, (MVMObject *)MVM_sc_get_obj_sc(tc, obj)); /* Update SC of the object, claiming it. */ - MVM_ASSIGN_REF(tc, obj, obj->header.sc, comp_sc); + MVM_sc_set_obj_sc(tc, obj, comp_sc); } } @@ -338,16 +348,16 @@ void MVM_sc_wb_hit_st(MVMThreadContext *tc, MVMSTable *st) { /* Otherwise, check that the STable's SC is different from the SC * of the compilation we're currently in. Repossess if so. */ comp_sc = (MVMSerializationContext *)MVM_repr_at_pos_o(tc, tc->compiling_scs, 0); - if (st->header.sc != comp_sc) { + if (MVM_sc_get_stable_sc(tc, st) != comp_sc) { /* Add to root set. */ MVMint64 new_slot = comp_sc->body->num_stables; MVM_sc_push_stable(tc, comp_sc, st); /* Add repossession entry. */ MVM_repr_push_i(tc, comp_sc->body->rep_indexes, (new_slot << 1) | 1); - MVM_repr_push_o(tc, comp_sc->body->rep_scs, (MVMObject *)st->header.sc); + MVM_repr_push_o(tc, comp_sc->body->rep_scs, (MVMObject *)MVM_sc_get_stable_sc(tc, st)); /* Update SC of the STable, claiming it. */ - MVM_ASSIGN_REF(tc, st, st->header.sc, comp_sc); + MVM_sc_set_stable_sc(tc, st, comp_sc); } } diff --git a/src/6model/sc.h b/src/6model/sc.h index 7edc67d..e449d17 100644 --- a/src/6model/sc.h +++ b/src/6model/sc.h @@ -18,6 +18,8 @@ MVMObject * MVM_sc_get_code(MVMThreadContext *tc, MVMSerializationContext *sc, M void MVM_sc_set_code(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx, MVMObject *code); void MVM_sc_set_code_list(MVMThreadContext *tc, MVMSerializationContext *sc, MVMObject *code_list); MVMuint64 MVM_sc_get_object_count(MVMThreadContext *tc, MVMSerializationContext *sc); +MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj); +MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable *st); void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationContext *sc); void MVM_sc_set_stable_sc(MVMThreadContext *tc, MVMSTable *st, MVMSerializationContext *sc); MVMSerializationContext * MVM_sc_find_by_handle(MVMThreadContext *tc, MVMString *handle); diff --git a/src/6model/serialization.c b/src/6model/serialization.c index 6d1b844..c508721 100644 --- a/src/6model/serialization.c +++ b/src/6model/serialization.c @@ -288,14 +288,14 @@ static MVMint32 get_sc_id(MVMThreadContext *tc, MVMSerializationWriter *writer, static void get_stable_ref_info(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMSTable *st, MVMint32 *sc, MVMint32 *sc_idx) { /* Add to this SC if needed. */ - if (st->header.sc == NULL) { - MVM_ASSIGN_REF(tc, st, st->header.sc, writer->root.sc); + if (MVM_sc_get_stable_sc(tc, st) == NULL) { + MVM_sc_set_stable_sc(tc, st, writer->root.sc); MVM_sc_push_stable(tc, writer->root.sc, st); } /* Work out SC reference. */ - *sc = get_sc_id(tc, writer, st->header.sc); - *sc_idx = (MVMint32)MVM_sc_find_stable_idx(tc, st->header.sc, st); + *sc = get_sc_id(tc, writer, MVM_sc_get_stable_sc(tc, st)); + *sc_idx = (MVMint32)MVM_sc_find_stable_idx(tc, MVM_sc_get_stable_sc(tc, st), st); } /* Expands current target storage as needed. */ @@ -352,20 +352,18 @@ static void write_str_func(MVMThreadContext *tc, MVMSerializationWriter *writer, *(writer->cur_write_offset) += 4; } -#define SC_OBJ(obj) ((obj)->header.sc) - /* Writes an object reference. */ static void write_obj_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *ref) { MVMint32 sc_id, idx; - if (OBJ_IS_NULL(SC_OBJ(ref))) { + if (OBJ_IS_NULL(MVM_sc_get_obj_sc(tc, ref))) { /* This object doesn't belong to an SC yet, so it must be serialized as part of * this compilation unit. Add it to the work list. */ MVM_sc_set_obj_sc(tc, ref, writer->root.sc); MVM_sc_push_object(tc, writer->root.sc, ref); } - sc_id = get_sc_id(tc, writer, SC_OBJ(ref)); - idx = (MVMint32)MVM_sc_find_object_idx(tc, SC_OBJ(ref), ref); + sc_id = get_sc_id(tc, writer, MVM_sc_get_obj_sc(tc, ref)); + idx = (MVMint32)MVM_sc_find_object_idx(tc, MVM_sc_get_obj_sc(tc, ref), ref); expand_storage_if_needed(tc, writer, 8); write_int32(*(writer->cur_write_buffer), *(writer->cur_write_offset), sc_id); @@ -457,7 +455,7 @@ static void write_hash_str_var(MVMThreadContext *tc, MVMSerializationWriter *wri /* Writes a reference to a code object in some SC. */ static void write_code_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *code) { - MVMSerializationContext *sc = code->header.sc; + MVMSerializationContext *sc = MVM_sc_get_obj_sc(tc, code); MVMint32 sc_id = get_sc_id(tc, writer, sc); MVMint32 idx = (MVMint32)MVM_sc_find_code_idx(tc, sc, code); expand_storage_if_needed(tc, writer, 8); @@ -472,7 +470,7 @@ static void write_code_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, static MVMObject * closure_to_static_code_ref(MVMThreadContext *tc, MVMObject *closure, MVMint64 fatal) { MVMObject *scr = (MVMObject *)(((MVMCode *)closure)->body.sf)->body.static_code; - if (scr == NULL || scr->header.sc == NULL) { + if (scr == NULL || MVM_sc_get_obj_sc(tc, scr) == NULL) { if (fatal) MVM_exception_throw_adhoc(tc, "Serialization Error: missing static code ref for closure '%s'", @@ -486,20 +484,20 @@ static MVMObject * closure_to_static_code_ref(MVMThreadContext *tc, MVMObject *c /* Takes an outer context that is potentially to be serialized. Checks if it * is of interest, and if so sets it up to be serialized. */ static MVMint32 get_serialized_context_idx(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *ctx) { - if (OBJ_IS_NULL(ctx->header.sc)) { + if (OBJ_IS_NULL(MVM_sc_get_obj_sc(tc, ctx))) { /* Make sure we should chase a level down. */ if (OBJ_IS_NULL(closure_to_static_code_ref(tc, ((MVMContext *)ctx)->body.context->code_ref, 0))) { return 0; } else { MVM_repr_push_o(tc, writer->contexts_list, ctx); - MVM_ASSIGN_REF(tc, ctx, ctx->header.sc, writer->root.sc); + MVM_sc_set_obj_sc(tc, ctx, writer->root.sc); return (MVMint32)MVM_repr_elems(tc, writer->contexts_list); } } else { MVMint64 i, c; - if (ctx->header.sc != writer->root.sc) + if (MVM_sc_get_obj_sc(tc, ctx) != writer->root.sc) MVM_exception_throw_adhoc(tc, "Serialization Error: reference to context outside of SC"); c = MVM_repr_elems(tc, writer->contexts_list); @@ -530,7 +528,7 @@ static void serialize_closure(MVMThreadContext *tc, MVMSerializationWriter *writ /* Locate the static code object. */ MVMObject *static_code_ref = closure_to_static_code_ref(tc, closure, 1); - MVMSerializationContext *static_code_sc = static_code_ref->header.sc; + MVMSerializationContext *static_code_sc = MVM_sc_get_obj_sc(tc, static_code_ref); /* Ensure there's space in the closures table; grow if not. */ MVMint32 offset = writer->root.num_closures * CLOSURES_TABLE_ENTRY_SIZE; @@ -554,14 +552,14 @@ static void serialize_closure(MVMThreadContext *tc, MVMSerializationWriter *writ if (((MVMCode *)closure)->body.code_object) { MVMObject *code_obj = (MVMObject *)((MVMCode *)closure)->body.code_object; write_int32(writer->root.closures_table, offset + 12, 1); - if (!code_obj->header.sc) { - MVM_ASSIGN_REF(tc, code_obj, code_obj->header.sc, writer->root.sc); + if (!MVM_sc_get_obj_sc(tc, code_obj)) { + MVM_sc_set_obj_sc(tc, code_obj, writer->root.sc); MVM_sc_push_object(tc, writer->root.sc, code_obj); } write_int32(writer->root.closures_table, offset + 16, - get_sc_id(tc, writer, code_obj->header.sc)); + get_sc_id(tc, writer, MVM_sc_get_obj_sc(tc, code_obj))); write_int32(writer->root.closures_table, offset + 20, - (MVMint32)MVM_sc_find_object_idx(tc, code_obj->header.sc, code_obj)); + (MVMint32)MVM_sc_find_object_idx(tc, MVM_sc_get_obj_sc(tc, code_obj), code_obj)); } else { write_int32(writer->root.closures_table, offset + 12, 0); @@ -572,7 +570,7 @@ static void serialize_closure(MVMThreadContext *tc, MVMSerializationWriter *writ /* Add the closure to this SC, and mark it as as being in it. */ MVM_repr_push_o(tc, writer->codes_list, closure); - MVM_ASSIGN_REF(tc, closure, closure->header.sc, writer->root.sc); + MVM_sc_set_obj_sc(tc, closure, writer->root.sc); } /* Writing function for references to things. */ @@ -607,11 +605,11 @@ void write_ref_func(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObj discrim = REFVAR_VM_HASH_STR_VAR; } else if (REPR(ref)->ID == MVM_REPR_ID_MVMCode) { - if (ref->header.sc && ((MVMCode *)ref)->body.is_static) { + if (MVM_sc_get_obj_sc(tc, ref) && ((MVMCode *)ref)->body.is_static) { /* Static code reference. */ discrim = REFVAR_STATIC_CODEREF; } - else if (ref->header.sc) { + else if (MVM_sc_get_obj_sc(tc, ref)) { /* Closure, but already seen and serialization already handled. */ discrim = REFVAR_CLONED_CODEREF; } @@ -930,7 +928,7 @@ static void serialize_context(MVMThreadContext *tc, MVMSerializationWriter *writ /* Locate the static code ref this context points to. */ MVMObject *static_code_ref = closure_to_static_code_ref(tc, frame->code_ref, 1); - MVMSerializationContext *static_code_sc = static_code_ref->header.sc; + MVMSerializationContext *static_code_sc = MVM_sc_get_obj_sc(tc, static_code_ref); if (OBJ_IS_NULL(static_code_sc)) MVM_exception_throw_adhoc(tc, "Serialization Error: closure outer is a code object not in an SC"); @@ -994,7 +992,7 @@ static void serialize_context(MVMThreadContext *tc, MVMSerializationWriter *writ MVM_exception_throw_adhoc(tc, "unsupported lexical type"); } } - MVM_ASSIGN_REF(tc, ctx, ctx->header.sc, writer->root.sc); + MVM_sc_set_obj_sc(tc, ctx, writer->root.sc); } /* Goes through the list of repossessions and serializes them all. */ @@ -2008,7 +2006,7 @@ static void repossess(MVMThreadContext *tc, MVMSerializationReader *reader, MVMi /* If we have a reposession conflict, make a copy of the original object * and reference it from the conflicts list. Push the original (about to * be overwritten) object reference too. */ - if (orig_obj->header.sc != orig_sc) { + if (MVM_sc_get_obj_sc(tc, orig_obj) != orig_sc) { MVMROOT(tc, orig_obj, { MVMObject *backup = NULL; MVMROOT(tc, backup, { @@ -2040,7 +2038,7 @@ static void repossess(MVMThreadContext *tc, MVMSerializationReader *reader, MVMi MVM_sc_set_stable(tc, reader->root.sc, read_int32(table_row, 4), orig_st); /* Make sure we don't have a reposession conflict. */ - if (orig_st->header.sc != orig_sc) + if (MVM_sc_get_stable_sc(tc, orig_st) != orig_sc) fail_deserialize(tc, reader, "STable conflict detected during deserialization.\n" "(Probable attempt to load two modules that cannot be loaded together)."); diff --git a/src/core/interp.c b/src/core/interp.c index 478bd64..1ae8310 100644 --- a/src/core/interp.c +++ b/src/core/interp.c @@ -3388,11 +3388,11 @@ void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContex "Must provide an SCRef operand to scsetobj"); MVM_sc_set_object(tc, (MVMSerializationContext *)sc, GET_REG(cur_op, 2).i64, obj); - if (STABLE(obj)->header.sc == NULL) { + if (MVM_sc_get_stable_sc(tc, STABLE(obj)) == NULL) { /* Need to claim the SC also; typical case for new type objects. */ MVMSTable *st = STABLE(obj); MVM_sc_push_stable(tc, (MVMSerializationContext *)sc, st); - MVM_ASSIGN_REF(tc, st, st->header.sc, sc); + MVM_sc_set_stable_sc(tc, st, (MVMSerializationContext *)sc); } cur_op += 6; goto NEXT; @@ -3405,7 +3405,7 @@ void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContex "Must provide an SCRef operand to scsetcode"); MVM_sc_set_code(tc, (MVMSerializationContext *)sc, GET_REG(cur_op, 2).i64, code); - MVM_ASSIGN_REF(tc, code, code->header.sc, sc); + MVM_sc_set_obj_sc(tc, code, (MVMSerializationContext *)sc); cur_op += 6; goto NEXT; } @@ -3465,13 +3465,12 @@ void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContex if (REPR(sc)->ID != MVM_REPR_ID_SCRef) MVM_exception_throw_adhoc(tc, "Must provide an SCRef operand to setobjsc"); - MVM_ASSIGN_REF(tc, obj, obj->header.sc, - (MVMSerializationContext *)sc); + MVM_sc_set_obj_sc(tc, obj, (MVMSerializationContext *)sc); cur_op += 4; goto NEXT; } OP(getobjsc): - GET_REG(cur_op, 0).o = (MVMObject *)GET_REG(cur_op, 2).o->header.sc; + GET_REG(cur_op, 0).o = (MVMObject *)MVM_sc_get_obj_sc(tc, GET_REG(cur_op, 2).o); cur_op += 4; goto NEXT; OP(serialize): { -- 1.8.4.2
>From e3835199dfdbbac85579c66adb23be583be0b591 Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Wed, 29 Jan 2014 21:55:40 +0100 Subject: [PATCH 07/10] Assert that no SC accesses happen on objects mid-GC. --- src/6model/sc.c | 12 ++++++++++++ src/6model/sc.h | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/6model/sc.c b/src/6model/sc.c index 93dc8a5..fdcc453 100644 --- a/src/6model/sc.c +++ b/src/6model/sc.c @@ -257,21 +257,33 @@ MVMuint64 MVM_sc_get_object_count(MVMThreadContext *tc, MVMSerializationContext /* Gets an object's SC. */ MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj) { + assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); + assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); + assert(!(obj->header.forwarder)); return obj->header.sc; } /* Gets an STables's SC. */ MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable *st) { + assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); + assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); + assert(!(st->header.forwarder)); return st->header.sc; } /* Sets an object's SC. */ void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationContext *sc) { + assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); + assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); + assert(!(obj->header.forwarder)); MVM_ASSIGN_REF(tc, obj, obj->header.sc, sc); } /* Sets an STable's SC. */ void MVM_sc_set_stable_sc(MVMThreadContext *tc, MVMSTable *st, MVMSerializationContext *sc) { + assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); + assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); + assert(!(st->header.forwarder)); MVM_ASSIGN_REF(tc, st, st->header.sc, sc); } diff --git a/src/6model/sc.h b/src/6model/sc.h index e449d17..e087a71 100644 --- a/src/6model/sc.h +++ b/src/6model/sc.h @@ -29,6 +29,9 @@ MVMSerializationContext * MVM_sc_get_sc(MVMThreadContext *tc, MVMCompUnit *cu, M #define MVM_SC_WB_OBJ(tc, obj) \ do { \ MVMObject *check = (MVMObject *)obj; \ + assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); \ + assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); \ + assert(!(obj->header.forwarder)); \ if (check->header.sc) \ MVM_sc_wb_hit_obj(tc, check); \ } while (0); @@ -36,6 +39,9 @@ void MVM_sc_wb_hit_obj(MVMThreadContext *tc, MVMObject *obj); #define MVM_SC_WB_ST(tc, st) \ do { \ MVMSTable *check = st; \ + assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); \ + assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); \ + assert(!(st->header.forwarder)); \ if (check->header.sc) \ MVM_sc_wb_hit_st(tc, check); \ } while (0); -- 1.8.4.2
>From cd45568bd715db9575cf548f3650f5a307c5cf13 Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Wed, 29 Jan 2014 22:04:01 +0100 Subject: [PATCH 08/10] Move struct MVMCollectable's MVMSerializationContext pointer into a union. --- src/6model/6model.h | 6 ++++-- src/6model/sc.c | 8 ++++---- src/6model/sc.h | 4 ++-- src/gc/collect.c | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/6model/6model.h b/src/6model/6model.h index 0d7084e..92f7c24 100644 --- a/src/6model/6model.h +++ b/src/6model/6model.h @@ -132,8 +132,10 @@ struct MVMCollectable { /* Forwarding pointer, for copying/compacting GC purposes. */ MVMCollectable *forwarder; - /* Pointer to the serialization context this collectable lives in, if any. */ - MVMSerializationContext *sc; + union { + /* Pointer to the serialization context this collectable lives in, if any. */ + MVMSerializationContext *sc; + } sc_forward_u; }; /* The common things every object has. */ diff --git a/src/6model/sc.c b/src/6model/sc.c index fdcc453..3960ed7 100644 --- a/src/6model/sc.c +++ b/src/6model/sc.c @@ -260,7 +260,7 @@ MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); assert(!(obj->header.forwarder)); - return obj->header.sc; + return obj->header.sc_forward_u.sc; } /* Gets an STables's SC. */ @@ -268,7 +268,7 @@ MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable * assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); assert(!(st->header.forwarder)); - return st->header.sc; + return st->header.sc_forward_u.sc; } /* Sets an object's SC. */ @@ -276,7 +276,7 @@ void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationCon assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); assert(!(obj->header.forwarder)); - MVM_ASSIGN_REF(tc, obj, obj->header.sc, sc); + MVM_ASSIGN_REF(tc, obj, obj->header.sc_forward_u.sc, sc); } /* Sets an STable's SC. */ @@ -284,7 +284,7 @@ void MVM_sc_set_stable_sc(MVMThreadContext *tc, MVMSTable *st, MVMSerializationC assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); assert(!(st->header.forwarder)); - MVM_ASSIGN_REF(tc, st, st->header.sc, sc); + MVM_ASSIGN_REF(tc, st, st->header.sc_forward_u.sc, sc); } /* Resolves an SC handle using the SC weakhash. */ diff --git a/src/6model/sc.h b/src/6model/sc.h index e087a71..4852ea7 100644 --- a/src/6model/sc.h +++ b/src/6model/sc.h @@ -32,7 +32,7 @@ MVMSerializationContext * MVM_sc_get_sc(MVMThreadContext *tc, MVMCompUnit *cu, M assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); \ assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); \ assert(!(obj->header.forwarder)); \ - if (check->header.sc) \ + if (check->header.sc_forward_u.sc) \ MVM_sc_wb_hit_obj(tc, check); \ } while (0); void MVM_sc_wb_hit_obj(MVMThreadContext *tc, MVMObject *obj); @@ -42,7 +42,7 @@ void MVM_sc_wb_hit_obj(MVMThreadContext *tc, MVMObject *obj); assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); \ assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); \ assert(!(st->header.forwarder)); \ - if (check->header.sc) \ + if (check->header.sc_forward_u.sc) \ MVM_sc_wb_hit_st(tc, check); \ } while (0); void MVM_sc_wb_hit_st(MVMThreadContext *tc, MVMSTable *st); diff --git a/src/gc/collect.c b/src/gc/collect.c index 1b6c9b2..b465bef 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -320,7 +320,7 @@ void MVM_gc_mark_collectable(MVMThreadContext *tc, MVMGCWorklist *worklist, MVMC assert(!new_addr->forwarder); assert(!(new_addr->flags & MVM_CF_FORWARDER_VALID)); assert(REPR(new_addr)); - MVM_gc_worklist_add(tc, worklist, &new_addr->sc); + MVM_gc_worklist_add(tc, worklist, &new_addr->sc_forward_u.sc); if (!(new_addr->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { /* Need to view it as an object in here. */ @@ -637,7 +637,7 @@ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc) { /* Type object; doesn't have anything extra that needs freeing. */ } else if (col->flags & MVM_CF_STABLE) { - if (col->sc == (MVMSerializationContext *)1) { + if (col->sc_forward_u.sc == (MVMSerializationContext *)1) { /* We marked it dead last time, kill it. */ MVM_6model_stable_gc_free(tc, (MVMSTable *)col); } @@ -648,7 +648,7 @@ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc) { 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; + col->sc_forward_u.sc = (MVMSerializationContext *)1; } /* Skip the freelist updating. */ cur_ptr += obj_size; -- 1.8.4.2
>From 3ca1fcd749cb1532fa40e376815c03df5b0bc78b Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Wed, 29 Jan 2014 01:21:41 +0100 Subject: [PATCH 09/10] Add a MVMSTable member to the union to hold the chain of STables to free. Previously these were stored in the forwarding pointer, which necessitated casts. --- src/6model/6model.h | 2 ++ src/gc/collect.c | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/6model/6model.h b/src/6model/6model.h index 92f7c24..564598d 100644 --- a/src/6model/6model.h +++ b/src/6model/6model.h @@ -135,6 +135,8 @@ struct MVMCollectable { union { /* Pointer to the serialization context this collectable lives in, if any. */ MVMSerializationContext *sc; + /* Used to chain STables queued to be freed. */ + MVMSTable *st; } sc_forward_u; }; diff --git a/src/gc/collect.c b/src/gc/collect.c index b465bef..a407a32 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -502,7 +502,7 @@ static void MVM_gc_collect_enqueue_stable_for_deletion(MVMThreadContext *tc, MVM MVMSTable *old_head; do { old_head = tc->instance->stables_to_free; - st->header.forwarder = (MVMCollectable *)old_head; + st->header.sc_forward_u.st = old_head; } while (!MVM_trycas(&tc->instance->stables_to_free, old_head, st)); } @@ -573,8 +573,8 @@ void MVM_gc_collect_free_stables(MVMThreadContext *tc) { MVMSTable *st = tc->instance->stables_to_free; while (st) { MVMSTable *st_to_free = st; - st = (MVMSTable *)st_to_free->header.forwarder; - st_to_free->header.forwarder = NULL; + st = st_to_free->header.sc_forward_u.st; + st_to_free->header.sc_forward_u.st = NULL; MVM_6model_stable_gc_free(tc, st_to_free); } tc->instance->stables_to_free = NULL; -- 1.8.4.2
>From 16f66e4ccd02f164fb8891a17ca0fbdf90f4fccc Mon Sep 17 00:00:00 2001 From: Nicholas Clark <n...@ccl4.org> Date: Thu, 6 Feb 2014 11:05:09 +0100 Subject: [PATCH 10/10] Move the forwarder to the union. This shrinks every Object and STable by 8 bytes on systems with 64 bit pointers, and 8 bytes on systems with 32 bit pointers that require 64 bit alignment. --- src/6model/6model.h | 10 ++-------- src/6model/sc.c | 4 ---- src/6model/sc.h | 2 -- src/gc/collect.c | 26 ++++++++++++-------------- src/gc/roots.c | 4 +++- 5 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/6model/6model.h b/src/6model/6model.h index 564598d..0899987 100644 --- a/src/6model/6model.h +++ b/src/6model/6model.h @@ -129,10 +129,9 @@ struct MVMCollectable { /* Object size, in bytes. */ MVMuint16 size; - /* Forwarding pointer, for copying/compacting GC purposes. */ - MVMCollectable *forwarder; - union { + /* Forwarding pointer, for copying/compacting GC purposes. */ + MVMCollectable *forwarder; /* Pointer to the serialization context this collectable lives in, if any. */ MVMSerializationContext *sc; /* Used to chain STables queued to be freed. */ @@ -147,11 +146,6 @@ struct MVMObject { /* The s-table for the object. */ MVMSTable *st; - - /* Padding for 32-bit systems. */ -#if !defined(_M_X64) && !defined(__amd64__) - MVMuint32 pad; -#endif }; /* An dummy object, mostly used to compute the offset of the data part of diff --git a/src/6model/sc.c b/src/6model/sc.c index 3960ed7..407e162 100644 --- a/src/6model/sc.c +++ b/src/6model/sc.c @@ -259,7 +259,6 @@ MVMuint64 MVM_sc_get_object_count(MVMThreadContext *tc, MVMSerializationContext MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj) { assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); - assert(!(obj->header.forwarder)); return obj->header.sc_forward_u.sc; } @@ -267,7 +266,6 @@ MVMSerializationContext * MVM_sc_get_obj_sc(MVMThreadContext *tc, MVMObject *obj MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable *st) { assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); - assert(!(st->header.forwarder)); return st->header.sc_forward_u.sc; } @@ -275,7 +273,6 @@ MVMSerializationContext * MVM_sc_get_stable_sc(MVMThreadContext *tc, MVMSTable * void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationContext *sc) { assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); - assert(!(obj->header.forwarder)); MVM_ASSIGN_REF(tc, obj, obj->header.sc_forward_u.sc, sc); } @@ -283,7 +280,6 @@ void MVM_sc_set_obj_sc(MVMThreadContext *tc, MVMObject *obj, MVMSerializationCon void MVM_sc_set_stable_sc(MVMThreadContext *tc, MVMSTable *st, MVMSerializationContext *sc) { assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); - assert(!(st->header.forwarder)); MVM_ASSIGN_REF(tc, st, st->header.sc_forward_u.sc, sc); } diff --git a/src/6model/sc.h b/src/6model/sc.h index 4852ea7..37503f7 100644 --- a/src/6model/sc.h +++ b/src/6model/sc.h @@ -31,7 +31,6 @@ MVMSerializationContext * MVM_sc_get_sc(MVMThreadContext *tc, MVMCompUnit *cu, M MVMObject *check = (MVMObject *)obj; \ assert(!(obj->header.flags & MVM_CF_SECOND_GEN_LIVE)); \ assert(!(obj->header.flags & MVM_CF_FORWARDER_VALID)); \ - assert(!(obj->header.forwarder)); \ if (check->header.sc_forward_u.sc) \ MVM_sc_wb_hit_obj(tc, check); \ } while (0); @@ -41,7 +40,6 @@ void MVM_sc_wb_hit_obj(MVMThreadContext *tc, MVMObject *obj); MVMSTable *check = st; \ assert(!(st->header.flags & MVM_CF_SECOND_GEN_LIVE)); \ assert(!(st->header.flags & MVM_CF_FORWARDER_VALID)); \ - assert(!(st->header.forwarder)); \ if (check->header.sc_forward_u.sc) \ MVM_sc_wb_hit_st(tc, check); \ } while (0); diff --git a/src/gc/collect.c b/src/gc/collect.c index a407a32..a670482 100644 --- a/src/gc/collect.c +++ b/src/gc/collect.c @@ -181,16 +181,16 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work /* If the item was already seen and copied, then it will have a * forwarding address already. Just update this pointer to the * new address and we're done. */ - assert(*item_ptr != item->forwarder); + assert(*item_ptr != item->sc_forward_u.forwarder); if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) { - if (*item_ptr != item->forwarder) { - GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : updating handle %p from %p to forwarder %p\n", item_ptr, item, item->forwarder); + if (*item_ptr != item->sc_forward_u.forwarder) { + GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : updating handle %p from %p to forwarder %p\n", item_ptr, item, item->sc_forward_u.forwarder); } else { - GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : already visited handle %p to forwarder %p\n", item_ptr, item->forwarder); + GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : already visited handle %p to forwarder %p\n", item_ptr, item->sc_forward_u.forwarder); } } - *item_ptr = item->forwarder; + *item_ptr = item->sc_forward_u.forwarder; continue; } else { /* If the pointer is already into tospace (the bit we've already @@ -216,7 +216,7 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work /* At this point, we didn't already see the object, which means we * need to take some action. Go on the generation... */ if (item_gen2) { - assert(!item->forwarder); + assert(!(item->flags & MVM_CF_FORWARDER_VALID)); /* It's in the second generation. We'll just mark it. */ new_addr = item; if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) { @@ -279,7 +279,8 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT) && new_addr != item) { GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : updating handle %p from referent %p (reprid %d) to %p\n", item_ptr, item, REPR(item)->ID, new_addr); } - *item_ptr = item->forwarder = new_addr; + *item_ptr = new_addr; + item->sc_forward_u.forwarder = new_addr; /* Set the flag on the copy of item *in fromspace* to mark that the forwarder pointer is valid. */ item->flags |= MVM_CF_FORWARDER_VALID; @@ -317,9 +318,8 @@ static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, Work void MVM_gc_mark_collectable(MVMThreadContext *tc, MVMGCWorklist *worklist, MVMCollectable *new_addr) { MVMuint16 i; - assert(!new_addr->forwarder); assert(!(new_addr->flags & MVM_CF_FORWARDER_VALID)); - assert(REPR(new_addr)); + /*assert(REPR(new_addr));*/ MVM_gc_worklist_add(tc, worklist, &new_addr->sc_forward_u.sc); if (!(new_addr->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { @@ -521,9 +521,7 @@ void MVM_gc_collect_free_nursery_uncopied(MVMThreadContext *tc, void *limit) { MVMuint8 dead = !(item->flags & MVM_CF_FORWARDER_VALID); if (!dead) - assert(item->forwarder != NULL); - else - assert(item->forwarder == NULL); + assert(item->sc_forward_u.forwarder != NULL); /* Now go by collectable type. */ if (!(item->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) { @@ -637,7 +635,7 @@ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc) { /* Type object; doesn't have anything extra that needs freeing. */ } else if (col->flags & MVM_CF_STABLE) { - if (col->sc_forward_u.sc == (MVMSerializationContext *)1) { + if (col->sc_forward_u.sc == (MVMSerializationContext *)3) { /* We marked it dead last time, kill it. */ MVM_6model_stable_gc_free(tc, (MVMSTable *)col); } @@ -648,7 +646,7 @@ void MVM_gc_collect_free_gen2_unmarked(MVMThreadContext *tc) { 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 = (MVMSerializationContext *)1; + col->sc_forward_u.sc = (MVMSerializationContext *)3; } /* Skip the freelist updating. */ cur_ptr += obj_size; diff --git a/src/gc/roots.c b/src/gc/roots.c index aa65cad..2622a59 100644 --- a/src/gc/roots.c +++ b/src/gc/roots.c @@ -242,8 +242,10 @@ void MVM_gc_root_gen2_cleanup(MVMThreadContext *tc) { MVMuint32 cur_survivor = 0; MVMuint32 i; for (i = 0; i < num_roots; i++) - if (gen2roots[i]->flags & MVM_CF_SECOND_GEN_LIVE) + if (gen2roots[i]->flags & MVM_CF_SECOND_GEN_LIVE) { + assert(!(gen2roots[i]->flags & MVM_CF_FORWARDER_VALID)); gen2roots[cur_survivor++] = gen2roots[i]; + } tc->num_gen2roots = cur_survivor; } -- 1.8.4.2