[project @ 1999-03-03 18:58:53 by sof]
[ghc-hetmet.git] / ghc / rts / GC.c
index 965ee09..cc1c797 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.35 1999/02/17 15:04:40 simonm Exp $
+ * $Id: GC.c,v 1.47 1999/03/03 18:58:53 sof Exp $
  *
  * (c) The GHC Team 1998-1999
  *
@@ -100,21 +100,23 @@ lnat g0s0_pcnt_kept = 30; /* percentage of g0s0 live at last minor GC */
    Static function declarations
    -------------------------------------------------------------------------- */
 
-static StgClosure *evacuate(StgClosure *q);
-static void    zeroStaticObjectList(StgClosure* first_static);
-static rtsBool traverse_weak_ptr_list(void);
-static void    zeroMutableList(StgMutClosure *first);
-static void    revertDeadCAFs(void);
+static StgClosure * evacuate                ( StgClosure *q );
+static void         zero_static_object_list ( StgClosure* first_static );
+static void         zero_mutable_list       ( StgMutClosure *first );
+static void         revert_dead_CAFs        ( void );
 
-static void           scavenge_stack(StgPtr p, StgPtr stack_end);
-static void           scavenge_large(step *step);
-static void           scavenge(step *step);
-static void           scavenge_static(void);
-static void           scavenge_mutable_list(generation *g);
-static void           scavenge_mut_once_list(generation *g);
+static rtsBool      traverse_weak_ptr_list  ( void );
+static void         cleanup_weak_ptr_list   ( void );
+
+static void         scavenge_stack          ( StgPtr p, StgPtr stack_end );
+static void         scavenge_large          ( step *step );
+static void         scavenge                ( step *step );
+static void         scavenge_static         ( void );
+static void         scavenge_mutable_list   ( generation *g );
+static void         scavenge_mut_once_list  ( generation *g );
 
 #ifdef DEBUG
-static void gcCAFs(void);
+static void         gcCAFs                  ( void );
 #endif
 
 /* -----------------------------------------------------------------------------
@@ -144,7 +146,7 @@ void GarbageCollect(void (*get_roots)(void))
 {
   bdescr *bd;
   step *step;
-  lnat live, allocated, collected = 0;
+  lnat live, allocated, collected = 0, copied = 0;
   nat g, s;
 
 #ifdef PROFILING
@@ -196,10 +198,10 @@ void GarbageCollect(void (*get_roots)(void))
   scavenged_static_objects = END_OF_STATIC_LIST;
 
   /* zero the mutable list for the oldest generation (see comment by
-   * zeroMutableList below).
+   * zero_mutable_list below).
    */
   if (major_gc) { 
-    zeroMutableList(generations[RtsFlags.GcFlags.generations-1].mut_once_list);
+    zero_mutable_list(generations[RtsFlags.GcFlags.generations-1].mut_once_list);
   }
 
   /* Save the old to-space if we're doing a two-space collection
@@ -243,11 +245,12 @@ void GarbageCollect(void (*get_roots)(void))
       step->hpLim     = step->hp + BLOCK_SIZE_W;
       step->hp_bd     = bd;
       step->to_space  = bd;
-      step->to_blocks = 1; /* ???? */
+      step->to_blocks = 1;
       step->scan      = bd->start;
       step->scan_bd   = bd;
       step->new_large_objects = NULL;
       step->scavenged_large_objects = NULL;
+      new_blocks++;
       /* mark the large objects as not evacuated yet */
       for (bd = step->large_objects; bd; bd = bd->link) {
        bd->evacuated = 0;
@@ -272,6 +275,7 @@ void GarbageCollect(void (*get_roots)(void))
        step->hp_bd = bd;
        step->blocks = bd;
        step->n_blocks = 1;
+       new_blocks++;
       }
       /* Set the scan pointer for older generations: remember we
        * still have to scavenge objects that have been promoted. */
@@ -335,7 +339,6 @@ void GarbageCollect(void (*get_roots)(void))
   /* Mark the weak pointer list, and prepare to detect dead weak
    * pointers.
    */
-  markWeakList();
   old_weak_ptr_list = weak_ptr_list;
   weak_ptr_list = NULL;
   weak_done = rtsFalse;
@@ -358,7 +361,7 @@ void GarbageCollect(void (*get_roots)(void))
        */
       scavengeEverything();
       /* revert dead CAFs and update enteredCAFs list */
-      revertDeadCAFs();
+      revert_dead_CAFs();
 #endif      
       markHugsObjects();
 #if 0
@@ -399,6 +402,9 @@ void GarbageCollect(void (*get_roots)(void))
     loop2:
       for (gen = RtsFlags.GcFlags.generations-1; gen >= 0; gen--) {
        for (st = generations[gen].n_steps-1; st >= 0 ; st--) {
+         if (gen == 0 && st == 0 && RtsFlags.GcFlags.generations > 1) { 
+           continue; 
+         }
          step = &generations[gen].steps[st];
          evac_gen = gen;
          if (step->hp_bd != step->scan_bd || step->scan < step->hp) {
@@ -422,7 +428,12 @@ void GarbageCollect(void (*get_roots)(void))
     }
   }
 
-  /* Now see which stable names are still alive
+  /* Final traversal of the weak pointer list (see comment by
+   * cleanUpWeakPtrList below).
+   */
+  cleanup_weak_ptr_list();
+
+  /* Now see which stable names are still alive.
    */
   gcStablePtrTable(major_gc);
 
@@ -453,6 +464,7 @@ void GarbageCollect(void (*get_roots)(void))
 
   /* run through all the generations/steps and tidy up 
    */
+  copied = new_blocks * BLOCK_SIZE_W;
   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
 
     if (g <= N) {
@@ -467,6 +479,11 @@ void GarbageCollect(void (*get_roots)(void))
        /* Tidy the end of the to-space chains */
        step->hp_bd->free = step->hp;
        step->hp_bd->link = NULL;
+       /* stats information: how much we copied */
+       if (g <= N) {
+         copied -= step->hp_bd->start + BLOCK_SIZE_W -
+           step->hp_bd->free;
+       }
       }
 
       /* for generations we collected... */
@@ -542,6 +559,18 @@ void GarbageCollect(void (*get_roots)(void))
   /* Guess the amount of live data for stats. */
   live = calcLive();
 
+  /* Free the small objects allocated via allocate(), since this will
+   * all have been copied into G0S1 now.  
+   */
+  if (small_alloc_list != NULL) {
+    freeChain(small_alloc_list);
+  }
+  small_alloc_list = NULL;
+  alloc_blocks = 0;
+  alloc_Hp = NULL;
+  alloc_HpLim = NULL;
+  alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
+
   /* Two-space collector:
    * Free the old to-space, and estimate the amount of live data.
    */
@@ -637,7 +666,7 @@ void GarbageCollect(void (*get_roots)(void))
   }
 
   /* revert dead CAFs and update enteredCAFs list */
-  revertDeadCAFs();
+  revert_dead_CAFs();
   
   /* mark the garbage collected CAFs as dead */
 #ifdef DEBUG
@@ -646,7 +675,7 @@ void GarbageCollect(void (*get_roots)(void))
   
   /* zero the scavenged static object list */
   if (major_gc) {
-    zeroStaticObjectList(scavenged_static_objects);
+    zero_static_object_list(scavenged_static_objects);
   }
 
   /* Reset the nursery
@@ -655,19 +684,10 @@ void GarbageCollect(void (*get_roots)(void))
     bd->free = bd->start;
     ASSERT(bd->gen == g0);
     ASSERT(bd->step == g0s0);
+    IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE));
   }
   current_nursery = g0s0->blocks;
 
-  /* Free the small objects allocated via allocate(), since this will
-   * all have been copied into G0S1 now.  
-   */
-  if (small_alloc_list != NULL) {
-    freeChain(small_alloc_list);
-  }
-  small_alloc_list = NULL;
-  alloc_blocks = 0;
-  alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
-
   /* start any pending finalizers */
   scheduleFinalizers(old_weak_ptr_list);
   
@@ -691,7 +711,7 @@ void GarbageCollect(void (*get_roots)(void))
   IF_DEBUG(sanity, memInventory());
 
   /* ok, GC over: tell the stats department what happened. */
-  stat_endGC(allocated, collected, live, N);
+  stat_endGC(allocated, collected, live, copied, N);
 }
 
 /* -----------------------------------------------------------------------------
@@ -731,6 +751,27 @@ traverse_weak_ptr_list(void)
   last_w = &old_weak_ptr_list;
   for (w = old_weak_ptr_list; w; w = next_w) {
 
+    /* First, this weak pointer might have been evacuated.  If so,
+     * remove the forwarding pointer from the weak_ptr_list.
+     */
+    if (get_itbl(w)->type == EVACUATED) {
+      w = (StgWeak *)((StgEvacuated *)w)->evacuee;
+      *last_w = w;
+    }
+
+    /* There might be a DEAD_WEAK on the list if finalizeWeak# was
+     * called on a live weak pointer object.  Just remove it.
+     */
+    if (w->header.info == &DEAD_WEAK_info) {
+      next_w = ((StgDeadWeak *)w)->link;
+      *last_w = next_w;
+      continue;
+    }
+
+    ASSERT(get_itbl(w)->type == WEAK);
+
+    /* Now, check whether the key is reachable.
+     */
     if ((new = isAlive(w->key))) {
       w->key = new;
       /* evacuate the value and finalizer */
@@ -769,6 +810,39 @@ traverse_weak_ptr_list(void)
 }
 
 /* -----------------------------------------------------------------------------
+   After GC, the live weak pointer list may have forwarding pointers
+   on it, because a weak pointer object was evacuated after being
+   moved to the live weak pointer list.  We remove those forwarding
+   pointers here.
+
+   Also, we don't consider weak pointer objects to be reachable, but
+   we must nevertheless consider them to be "live" and retain them.
+   Therefore any weak pointer objects which haven't as yet been
+   evacuated need to be evacuated now.
+   -------------------------------------------------------------------------- */
+
+static void
+cleanup_weak_ptr_list ( void )
+{
+  StgWeak *w, **last_w;
+
+  last_w = &weak_ptr_list;
+  for (w = weak_ptr_list; w; w = w->link) {
+
+    if (get_itbl(w)->type == EVACUATED) {
+      w = (StgWeak *)((StgEvacuated *)w)->evacuee;
+      *last_w = w;
+    }
+
+    if (Bdescr((P_)w)->evacuated == 0) {
+      (StgClosure *)w = evacuate((StgClosure *)w);
+      *last_w = w;
+    }
+    last_w = &(w->link);
+  }
+}
+
+/* -----------------------------------------------------------------------------
    isAlive determines whether the given closure is still alive (after
    a garbage collection) or not.  It returns the new address of the
    closure if it is alive, or NULL otherwise.
@@ -842,6 +916,13 @@ static void addBlock(step *step)
   new_blocks++;
 }
 
+static __inline__ void 
+upd_evacuee(StgClosure *p, StgClosure *dest)
+{
+  p->header.info = &EVACUATED_info;
+  ((StgEvacuated *)p)->evacuee = dest;
+}
+
 static __inline__ StgClosure *
 copy(StgClosure *src, nat size, step *step)
 {
@@ -854,7 +935,11 @@ copy(StgClosure *src, nat size, step *step)
    * by evacuate()).
    */
   if (step->gen->no < evac_gen) {
+#ifdef NO_EAGER_PROMOTION    
+    failed_to_evac = rtsTrue;
+#else
     step = &generations[evac_gen].steps[0];
+#endif
   }
 
   /* chain a new block onto the to-space for the destination step if
@@ -870,6 +955,7 @@ copy(StgClosure *src, nat size, step *step)
 
   dest = step->hp;
   step->hp = to;
+  upd_evacuee(src,(StgClosure *)dest);
   return (StgClosure *)dest;
 }
 
@@ -885,7 +971,11 @@ copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, step *step)
 
   TICK_GC_WORDS_COPIED(size_to_copy);
   if (step->gen->no < evac_gen) {
+#ifdef NO_EAGER_PROMOTION    
+    failed_to_evac = rtsTrue;
+#else
     step = &generations[evac_gen].steps[0];
+#endif
   }
 
   if (step->hp + size_to_reserve >= step->hpLim) {
@@ -898,18 +988,10 @@ copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, step *step)
   
   dest = step->hp;
   step->hp += size_to_reserve;
+  upd_evacuee(src,(StgClosure *)dest);
   return (StgClosure *)dest;
 }
 
-static __inline__ void 
-upd_evacuee(StgClosure *p, StgClosure *dest)
-{
-  StgEvacuated *q = (StgEvacuated *)p;
-
-  SET_INFO(q,&EVACUATED_info);
-  q->evacuee = dest;
-}
-
 /* -----------------------------------------------------------------------------
    Evacuate a large object
 
@@ -957,7 +1039,11 @@ evacuate_large(StgPtr p, rtsBool mutable)
    */
   step = bd->step->to;
   if (step->gen->no < evac_gen) {
+#ifdef NO_EAGER_PROMOTION    
+    failed_to_evac = rtsTrue;
+#else
     step = &generations[evac_gen].steps[0];
+#endif
   }
 
   bd->step = step;
@@ -1039,7 +1125,7 @@ evacuate(StgClosure *q)
   const StgInfoTable *info;
 
 loop:
-  if (!LOOKS_LIKE_STATIC(q)) {
+  if (HEAP_ALLOCED(q)) {
     bd = Bdescr((P_)q);
     if (bd->gen->no > N) {
       /* Can't evacuate this object, because it's in a generation
@@ -1064,46 +1150,42 @@ loop:
   switch (info -> type) {
 
   case BCO:
-    to = copy(q,bco_sizeW(stgCast(StgBCO*,q)),step);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,bco_sizeW(stgCast(StgBCO*,q)),step);
 
   case MUT_VAR:
     ASSERT(q->header.info != &MUT_CONS_info);
   case MVAR:
     to = copy(q,sizeW_fromITBL(info),step);
-    upd_evacuee(q,to);
     recordMutable((StgMutClosure *)to);
     return to;
 
-  case STABLE_NAME:
-    stable_ptr_table[((StgStableName *)q)->sn].keep = rtsTrue;
-    to = copy(q,sizeofW(StgStableName),step);
-    upd_evacuee(q,to);
-    return to;
-
   case FUN_1_0:
   case FUN_0_1:
   case CONSTR_1_0:
   case CONSTR_0_1:
-    to = copy(q,sizeofW(StgHeader)+1,step);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,sizeofW(StgHeader)+1,step);
 
   case THUNK_1_0:              /* here because of MIN_UPD_SIZE */
   case THUNK_0_1:
-  case FUN_1_1:
-  case FUN_0_2:
-  case FUN_2_0:
   case THUNK_1_1:
   case THUNK_0_2:
   case THUNK_2_0:
+#ifdef NO_PROMOTE_THUNKS
+    if (bd->gen->no == 0 && 
+       bd->step->no != 0 &&
+       bd->step->no == bd->gen->n_steps-1) {
+      step = bd->step;
+    }
+#endif
+    return copy(q,sizeofW(StgHeader)+2,step);
+
+  case FUN_1_1:
+  case FUN_0_2:
+  case FUN_2_0:
   case CONSTR_1_1:
   case CONSTR_0_2:
   case CONSTR_2_0:
-    to = copy(q,sizeofW(StgHeader)+2,step);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,sizeofW(StgHeader)+2,step);
 
   case FUN:
   case THUNK:
@@ -1114,19 +1196,15 @@ loop:
   case CAF_ENTERED:
   case WEAK:
   case FOREIGN:
-    to = copy(q,sizeW_fromITBL(info),step);
-    upd_evacuee(q,to);
-    return to;
+  case STABLE_NAME:
+    return copy(q,sizeW_fromITBL(info),step);
 
   case CAF_BLACKHOLE:
   case BLACKHOLE:
-    to = copyPart(q,BLACKHOLE_sizeW(),sizeofW(StgHeader),step);
-    upd_evacuee(q,to);
-    return to;
+    return copyPart(q,BLACKHOLE_sizeW(),sizeofW(StgHeader),step);
 
   case BLACKHOLE_BQ:
     to = copy(q,BLACKHOLE_sizeW(),step); 
-    upd_evacuee(q,to);
     recordMutable((StgMutClosure *)to);
     return to;
 
@@ -1146,11 +1224,11 @@ loop:
       case CONSTR_0_2:
       case CONSTR_STATIC:
        { 
-         StgNat32 offset = info->layout.selector_offset;
+         StgWord32 offset = info->layout.selector_offset;
 
          /* check that the size is in range */
          ASSERT(offset < 
-                (StgNat32)(selectee_info->layout.payload.ptrs + 
+                (StgWord32)(selectee_info->layout.payload.ptrs + 
                            selectee_info->layout.payload.nptrs));
 
          /* perform the selection! */
@@ -1160,7 +1238,7 @@ loop:
           * with the evacuation, just update the source address with
           * a pointer to the (evacuated) constructor field.
           */
-         if (IS_USER_PTR(q)) {
+         if (HEAP_ALLOCED(q)) {
            bdescr *bd = Bdescr((P_)q);
            if (bd->evacuated) {
              if (bd->gen->no < evac_gen) {
@@ -1213,9 +1291,7 @@ loop:
        barf("evacuate: THUNK_SELECTOR: strange selectee");
       }
     }
-    to = copy(q,THUNK_SELECTOR_sizeW(),step);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,THUNK_SELECTOR_sizeW(),step);
 
   case IND:
   case IND_OLDGEN:
@@ -1273,9 +1349,7 @@ loop:
   case PAP:
     /* these are special - the payload is a copy of a chunk of stack,
        tagging and all. */
-    to = copy(q,pap_sizeW(stgCast(StgPAP*,q)),step);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,pap_sizeW(stgCast(StgPAP*,q)),step);
 
   case EVACUATED:
     /* Already evacuated, just return the forwarding address.
@@ -1304,9 +1378,7 @@ loop:
        return q;
       } else {
        /* just copy the block */
-       to = copy(q,size,step);
-       upd_evacuee(q,to);
-       return to;
+       return copy(q,size,step);
       }
     }
 
@@ -1321,7 +1393,6 @@ loop:
       } else {
        /* just copy the block */
        to = copy(q,size,step);
-       upd_evacuee(q,to);
        if (info->type == MUT_ARR_PTRS) {
          recordMutable((StgMutClosure *)to);
        }
@@ -1355,7 +1426,6 @@ loop:
        new_tso->splim = (StgPtr)new_tso->splim + diff;
        
        relocate_TSO(tso, new_tso);
-       upd_evacuee(q,(StgClosure *)new_tso);
 
        recordMutable((StgMutClosure *)new_tso);
        return (StgClosure *)new_tso;
@@ -1438,7 +1508,24 @@ scavenge_srt(const StgInfoTable *info)
   srt = stgCast(StgClosure **,info->srt);
   srt_end = srt + info->srt_len;
   for (; srt < srt_end; srt++) {
-    evacuate(*srt);
+    /* Special-case to handle references to closures hiding out in DLLs, since
+       double indirections required to get at those. The code generator knows
+       which is which when generating the SRT, so it stores the (indirect)
+       reference to the DLL closure in the table by first adding one to it.
+       We check for this here, and undo the addition before evacuating it.
+
+       If the SRT entry hasn't got bit 0 set, the SRT entry points to a
+       closure that's fixed at link-time, and no extra magic is required.
+    */
+#ifdef HAVE_WIN32_DLL_SUPPORT
+    if ( stgCast(unsigned long,*srt) & 0x1 ) {
+       evacuate(*stgCast(StgClosure**,(stgCast(unsigned long, *srt) & ~0x1)));
+    } else {
+       evacuate(*srt);
+    }
+#else
+       evacuate(*srt);
+#endif
   }
 }
 
@@ -2155,7 +2242,7 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
 {
   StgPtr q;
   const StgInfoTable* info;
-  StgNat32 bitmap;
+  StgWord32 bitmap;
 
   /* 
    * Each time around this loop, we are looking at a chunk of stack
@@ -2174,14 +2261,13 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
      
     /* Is q a pointer to a closure?
      */
-    if (! LOOKS_LIKE_GHC_INFO(q)) {
 
+    if (! LOOKS_LIKE_GHC_INFO(q)) {
 #ifdef DEBUG
-      if (LOOKS_LIKE_STATIC(q)) { /* Is it a static closure? */
+      if ( 0 && LOOKS_LIKE_STATIC_CLOSURE(q) ) {  /* Is it a static closure? */
        ASSERT(closure_STATIC(stgCast(StgClosure*,q)));
-      } 
-      /* otherwise, must be a pointer into the allocation space.
-       */
+      }
+      /* otherwise, must be a pointer into the allocation space. */
 #endif
 
       (StgClosure *)*p = evacuate((StgClosure *)q);
@@ -2233,18 +2319,24 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
            }
            continue;
          }
-         step = bd->step->to;
+
+         /* Don't promote blackholes */
+         step = bd->step;
+         if (!(step->gen->no == 0 && 
+               step->no != 0 &&
+               step->no == step->gen->n_steps-1)) {
+           step = step->to;
+         }
+
          switch (type) {
          case BLACKHOLE:
          case CAF_BLACKHOLE:
            to = copyPart(frame->updatee, BLACKHOLE_sizeW(), 
                          sizeofW(StgHeader), step);
-           upd_evacuee(frame->updatee,to);
            frame->updatee = to;
            continue;
          case BLACKHOLE_BQ:
            to = copy(frame->updatee, BLACKHOLE_sizeW(), step);
-           upd_evacuee(frame->updatee,to);
            frame->updatee = to;
            recordMutable((StgMutClosure *)to);
            continue;
@@ -2416,7 +2508,7 @@ scavenge_large(step *step)
 }
 
 static void
-zeroStaticObjectList(StgClosure* first_static)
+zero_static_object_list(StgClosure* first_static)
 {
   StgClosure* p;
   StgClosure* link;
@@ -2438,7 +2530,7 @@ zeroStaticObjectList(StgClosure* first_static)
  * mutable list.
  */
 static void
-zeroMutableList(StgMutClosure *first)
+zero_mutable_list( StgMutClosure *first )
 {
   StgMutClosure *next, *c;
 
@@ -2465,7 +2557,7 @@ void RevertCAFs(void)
   }
 }
 
-void revertDeadCAFs(void)
+void revert_dead_CAFs(void)
 {
     StgCAF* caf = enteredCAFs;
     enteredCAFs = END_CAF_LIST;
@@ -2489,7 +2581,7 @@ void revertDeadCAFs(void)
                break;
            }
        default:
-               barf("revertDeadCAFs: enteredCAFs list corrupted");
+               barf("revert_dead_CAFs: enteredCAFs list corrupted");
        } 
        caf = next;
     }