diff --git a/includes/rts/Constants.h b/includes/rts/Constants.h index 15ff2a43d9f1301eab80a0f7d1a7d9c1244b78ae..c2cad8fc80893b29b24a021dd4e3769f16788ebb 100644 --- a/includes/rts/Constants.h +++ b/includes/rts/Constants.h @@ -22,9 +22,11 @@ /* ----------------------------------------------------------------------------- Minimum closure sizes - This is the minimum number of words in the payload of a - heap-allocated closure, so that the closure has enough room to be - overwritten with a forwarding pointer during garbage collection. + This is the minimum number of words in the payload of a heap-allocated + closure, so that the closure has two bits in the bitmap for mark-compact + collection. + + See Note [Mark bits in mark-compact collector] in rts/sm/Compact.h -------------------------------------------------------------------------- */ #define MIN_PAYLOAD_SIZE 1 diff --git a/rts/sm/Compact.c b/rts/sm/Compact.c index 3bfefa7ceb5674ab3a5b53976b21949483ebedc6..927a50595aec63973cbb4ab6ef309bf0bd805528 100644 --- a/rts/sm/Compact.c +++ b/rts/sm/Compact.c @@ -830,10 +830,11 @@ update_fwd_compact( bdescr *blocks ) size = p - q; if (free + size > free_bd->start + BLOCK_SIZE_W) { - // set the next bit in the bitmap to indicate that - // this object needs to be pushed into the next - // block. This saves us having to run down the - // threaded info pointer list twice during the next pass. + // set the next bit in the bitmap to indicate that this object + // needs to be pushed into the next block. This saves us having + // to run down the threaded info pointer list twice during the + // next pass. See Note [Mark bits in mark-compact collector] in + // Compact.h. mark(q+1,bd); free_bd = free_bd->link; free = free_bd->start; diff --git a/rts/sm/Compact.h b/rts/sm/Compact.h index b0521122df3c20ec07d12916057e4271906b55f6..be9a09d4abfac41001e9fb0814fa2e85a8426425 100644 --- a/rts/sm/Compact.h +++ b/rts/sm/Compact.h @@ -15,6 +15,45 @@ #include "BeginPrivate.h" +/* ----------------------------------------------------------------------------- + Note [Mark bits in mark-compact collector] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + In mark-compact collector each closure has two mark bits: + + - Liveness bit: not marked == unreachable (dead) + + - "Too large" bit: when this is set it means that the closure won't fit in + the current heap block and we need to move it to the next block in the + heap. + + Why do we need the second bit? We only know a closure's size *before* + threading it, because after threading the info table pointer will be end of + the chain. So by the time we do the second pass to move the closures and + unthread chains we'd have to do two passes, one for to get the info table + pointer at the end of the chain to compute the closure size and update the + free pointer if it's too large to fit in the current block, and then another + pass to actually unthread. + + To avoid this we update the second bit when we first visit an object (in the + "forward" pass) and realize that it won't fit in the current block, and check + that bit in the second pass (where we actually move the object and update all + references). If the bit is set we move the object to the free location in the + next block in heap chain, otherwise we use the free pointer in the current + block. + + Instead of allocating two bits per object, we use the fact that most heap + closures will be at least two words (as one-word closures will mostly be + static objects), and allocate one bit per word in the heap. In the rare cases + where we allocate single-word heap objects (e.g. a non-top-level FUN with + empty payload) we add one non-pointer field to the payload so that the object + will have two words. The minimum amount of words in the payload is defined in + includes/rts/Constants.h as MIN_PAYLOAD_SIZE. + + (See also !1701 where we discussed lifting this restriction and allocating + two bits per object) + -------------------------------------------------------------------------- */ + INLINE_HEADER void mark(StgPtr p, bdescr *bd) {