[project @ 1999-01-13 17:25:37 by simonm]
authorsimonm <unknown>
Wed, 13 Jan 1999 17:25:59 +0000 (17:25 +0000)
committersimonm <unknown>
Wed, 13 Jan 1999 17:25:59 +0000 (17:25 +0000)
Added a generational garbage collector.

The collector is reliable but fairly untuned as yet.  It works with an
arbitrary number of generations: use +RTS -G<gens> to change the
number of generations used (default 2).

Stats: +RTS -Sstderr is quite useful, but to really see what's going
on compile the RTS with -DDEBUG and use +RTS -D32.

ARR_PTRS removed - it wasn't used anywhere.

Sanity checking improved:
- free blocks are now spammed when sanity checking is turned on
- a check for leaking blocks is performed after each GC.

33 files changed:
ghc/docs/users_guide/runtime_control.vsgml
ghc/driver/ghc.lprl
ghc/includes/Block.h
ghc/includes/ClosureMacros.h
ghc/includes/ClosureTypes.h
ghc/includes/Closures.h
ghc/includes/InfoTables.h
ghc/includes/PrimOps.h
ghc/includes/Rts.h
ghc/includes/StgMiscClosures.h
ghc/includes/TSO.h
ghc/includes/Updates.h
ghc/rts/BlockAlloc.c
ghc/rts/BlockAlloc.h
ghc/rts/DebugProf.c
ghc/rts/GC.c
ghc/rts/MBlock.c
ghc/rts/MBlock.h
ghc/rts/Makefile
ghc/rts/PrimOps.hc
ghc/rts/RtsFlags.c
ghc/rts/RtsFlags.h
ghc/rts/Sanity.c
ghc/rts/Sanity.h
ghc/rts/Schedule.c
ghc/rts/Stats.c
ghc/rts/Stats.h
ghc/rts/StgMiscClosures.hc
ghc/rts/Storage.c
ghc/rts/Storage.h
ghc/rts/StoragePriv.h
ghc/rts/Updates.hc
ghc/rts/Weak.c

index 19967ec..82876bf 100644 (file)
@@ -63,18 +63,26 @@ operation, but there are several things that can be tweaked for
 maximum performance.
 
 <descrip>
+<tag>@-G<generations>@:</tag>
+<nidx>-G&lt;generations&gt; RTS option</nidx>
+<nidx>generations, number of</nidx>
+
+[Default: 2] Set the number of generations used by the garbage
+collector.  The default of 2 seems to be good, but the garbage
+collector can support any number of generations.  NOTE: -G1 (i.e. a
+two-space copying collector) is currently not supported.
+
 <tag>@-A<size>@:</tag>
 <nidx>-A&lt;size&gt; RTS option</nidx>
 <nidx>allocation area, size</nidx>
 
-[Default: 256k] Set the minimum (and initial) allocation area size
-used by the garbage collector.  The allocation area is resized after
-each garbage collection to be a multiple of the size of the current
-live data (currently a factor of 2).
+[Default: 256k] Set the allocation area size used by the garbage
+collector.  The allocation area (actually generation 0 step 0) is
+fixed and is never resized.
 
-Increasing the minimum allocation area size will typically give better
-performance for programs which quickly generate a large amount of live
-data.
+Increasing the allocation area size may or may not give better
+performance (a bigger allocation area means worse cache behaviour but
+fewer garbage collections and less promotion).
 
 <tag>@-k<size>@:</tag>
 <nidx>-k&lt;size&gt; RTS option</nidx>
@@ -132,26 +140,26 @@ heap size based on the current amount of live data.
 %PostScript), using the @stat2resid@<nidx>stat2resid</nidx> utility in
 %the GHC distribution (@ghc/utils/stat2resid@).
 
-<tag>@-F2s@:</tag> 
-<nidx>-F2s RTS option</nidx>
-
-Forces a program compiled for generational GC to use two-space copying
-collection. The two-space collector may outperform the generational
-collector for programs which have a very low heap residency. It can
-also be used to generate a statistics file from which a basic heap
-residency profile can be produced (see Section <ref name="stat2resid -
-residency info from GC stats" id="stat2resid">).
-
-There will still be a small execution overhead imposed by the
-generational compilation as the test for old generation updates will
-still be executed (of course none will actually happen).  This
-overhead is typically less than 1\%.
-
-<tag>@-j<size>@:</tag>
-<nidx>-j&lt;size&gt; RTS option</nidx>
-Force a major garbage collection every @<size>@ bytes.  (Normally
-used because you're keen on getting major-GC stats, notably heap residency
-info.)
+% <tag>@-F2s@:</tag> 
+% <nidx>-F2s RTS option</nidx>
+% 
+% Forces a program compiled for generational GC to use two-space copying
+% collection. The two-space collector may outperform the generational
+% collector for programs which have a very low heap residency. It can
+% also be used to generate a statistics file from which a basic heap
+% residency profile can be produced (see Section <ref name="stat2resid -
+% residency info from GC stats" id="stat2resid">).
+% 
+% There will still be a small execution overhead imposed by the
+% generational compilation as the test for old generation updates will
+% still be executed (of course none will actually happen).  This
+% overhead is typically less than 1\%.
+% 
+% <tag>@-j<size>@:</tag>
+% <nidx>-j&lt;size&gt; RTS option</nidx>
+% Force a major garbage collection every @<size>@ bytes.  (Normally
+% used because you're keen on getting major-GC stats, notably heap residency
+% info.)
 
 </descrip>
 
index 6ea5667..957c900 100644 (file)
@@ -2301,7 +2301,7 @@ sub process_ghc_timings {
            $MaxResidency = $1; $ResidencySamples = $2;
        }
 
-        $GCs = $1 if /^\s*([0-9,]+) garbage collections? performed/;
+        $GCs = $1 if /^\s*([0-9,]+) (collections? in generation 0|garbage collections? performed)/;
 
        # The presence of -? in the following pattern is only there to
        # accommodate 0.29 && <= 2.05 RTS'
index b8a0260..113145e 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Block.h,v 1.2 1998/12/02 13:20:53 simonm Exp $
+ * $Id: Block.h,v 1.3 1999/01/13 17:25:51 simonm Exp $
  *
  * Block structure for the storage manager
  *
@@ -43,13 +43,14 @@ typedef struct _bdescr {
   StgPtr free;                 /* first free byte of memory */
   struct _bdescr *link;                /* used for chaining blocks together */
   struct _bdescr *back;                /* used (occasionally) for doubly-linked lists*/
-  StgNat32 gen;                        /* generation */
-  StgNat32 step;               /* step */
+  struct _generation *gen;     /* generation */
+  struct _step *step;          /* step */
   StgNat32 blocks;             /* no. of blocks (if grp head, 0 otherwise) */
+  StgNat32 evacuated;           /* block is in to-space */
 #if SIZEOF_VOID_P == 8
-  StgNat32 _padding[5];
+  StgNat32 _padding[2];
 #else
-  StgNat32 _padding[1];
+  StgNat32 _padding[0];
 #endif
 } bdescr;
 
index 9d8f6cf..76bec3e 100644 (file)
@@ -1,5 +1,5 @@
 /* ----------------------------------------------------------------------------
- * $Id: ClosureMacros.h,v 1.2 1998/12/02 13:20:58 simonm Exp $
+ * $Id: ClosureMacros.h,v 1.3 1999/01/13 17:25:52 simonm Exp $
  *
  * Macros for building and manipulating closures
  *
@@ -186,8 +186,8 @@ static __inline__ StgOffset pap_sizeW( StgPAP* x )
  */
 static __inline__ StgOffset arr_words_sizeW( StgArrWords* x )
 { return sizeofW(StgArrWords) + x->words; }
-static __inline__ StgOffset arr_ptrs_sizeW( StgArrPtrs* x )
-{ return sizeofW(StgArrPtrs) + x->ptrs; }
+static __inline__ StgOffset mut_arr_ptrs_sizeW( StgMutArrPtrs* x )
+{ return sizeofW(StgMutArrPtrs) + x->ptrs; }
 
 static __inline__ StgWord bco_sizeW( StgBCO* bco )
 { return BCO_sizeW(bco->n_ptrs,bco->n_words,bco->n_instrs); }
@@ -241,8 +241,6 @@ static __inline__ StgWord tso_sizeW ( StgTSO *tso )
        SET_TICKY_HDR((StgClosure *)(c),0);             \
    }
 
-/* works for all ARR_WORDS, ARR_PTRS variants (at the moment...) */
-
 #define SET_ARR_HDR(c,info,costCentreStack,n_words) \
    SET_HDR(c,info,costCentreStack); \
    (c)->words = n_words;
index 495ca27..c91b9b2 100644 (file)
@@ -1,5 +1,5 @@
 /* ----------------------------------------------------------------------------
- * $Id: ClosureTypes.h,v 1.2 1998/12/02 13:20:58 simonm Exp $
+ * $Id: ClosureTypes.h,v 1.3 1999/01/13 17:25:52 simonm Exp $
  * 
  * Closure Type Constants
  *
 
 /* Object tag 0 raises an internal error */
 #define INVALID_OBJECT          0
-
 #define CONSTR                  1
 /* #define CONSTR_p_np */       
 #define CONSTR_INTLIKE         2
 #define CONSTR_CHARLIKE                3
 #define CONSTR_STATIC          4
 #define CONSTR_NOCAF_STATIC     5
-
 #define FUN                    6
 #define FUN_STATIC             7
-
 #define THUNK                  8
 /* #define THUNK_p_np */        
 #define THUNK_STATIC           9
 #define THUNK_SELECTOR         10
-
 #define BCO                    11
-
 #define AP_UPD                 12
 #define PAP                    13
-
 #define IND                    14
 #define IND_OLDGEN             15
 #define IND_PERM               16
 #define IND_OLDGEN_PERM                17
 #define IND_STATIC             18
-
 #define CAF_UNENTERED           19
 #define CAF_ENTERED            20
 #define CAF_BLACKHOLE          21
-
 #define RET_BCO                 22
 #define RET_SMALL              23
 #define RET_VEC_SMALL          24
 #define RET_VEC_BIG            26
 #define RET_DYN                        27
 #define UPDATE_FRAME           28
-#define CATCH_FRAME            29
-#define STOP_FRAME             30
-#define SEQ_FRAME              31
-
-#define BLACKHOLE              32
-#define MVAR                   33
-
-#define ARR_WORDS              34
-#define ARR_PTRS               35
-
-#define MUT_ARR_WORDS          36
-#define MUT_ARR_PTRS           37
-#define MUT_ARR_PTRS_FROZEN     38
-#define MUT_VAR                        39
-
-#define WEAK                   40
-#define FOREIGN                        41
-
-#define TSO                    42
-#define BLOCKED_FETCH          43
-#define FETCH_ME                44
-
-#define EVACUATED               45
+#define UPDATE_STATIC_FRAME     29
+#define CATCH_FRAME            30
+#define STOP_FRAME             31
+#define SEQ_FRAME              32
+#define BLACKHOLE              33
+#define BLACKHOLE_STATIC        34
+#define MVAR                   35
+#define ARR_WORDS              36
+#define MUT_ARR_WORDS          37
+#define MUT_ARR_PTRS           38
+#define MUT_ARR_PTRS_FROZEN     39
+#define MUT_VAR                        40
+#define WEAK                   41
+#define FOREIGN                        42
+#define TSO                    43
+#define BLOCKED_FETCH          44
+#define FETCH_ME                45
+#define EVACUATED               46
 
 #endif CLOSURETYPES_H
index a60fe28..f77ce9a 100644 (file)
@@ -1,5 +1,5 @@
 /* ----------------------------------------------------------------------------
- * $Id: Closures.h,v 1.2 1998/12/02 13:20:59 simonm Exp $
+ * $Id: Closures.h,v 1.3 1999/01/13 17:25:53 simonm Exp $
  *
  * Closures
  *
@@ -113,6 +113,19 @@ typedef struct StgClosure_ {
     struct StgClosure_ *payload[0];
 } StgClosure;
 
+/* What a stroke of luck - all our mutable closures follow the same
+ * basic layout, with the mutable link field as the second field after
+ * the header.  This means the following structure is the supertype of
+ * mutable closures.
+ */
+
+typedef struct StgMutClosure_ {
+    StgHeader   header;
+    StgPtr     *padding;
+    struct StgMutClosure_ *mut_link;
+    struct StgClosure_ *payload[0];
+} StgMutClosure;
+
 typedef struct {
     StgHeader   header;
     StgClosure *selectee;
@@ -147,8 +160,8 @@ typedef struct {
 
 typedef struct {
     StgHeader   header;
-    StgClosure *mut_link;
     StgClosure *indirectee;
+    StgMutClosure *mut_link;
 } StgIndOldGen;
 
 typedef struct {
@@ -178,12 +191,14 @@ typedef struct {
 typedef struct {
     StgHeader   header;
     StgWord     ptrs;
+    StgMutClosure *mut_link;   /* mutable list */
     StgClosure *payload[0];
-} StgArrPtrs;
+} StgMutArrPtrs;
 
 typedef struct {
     StgHeader   header;
     StgClosure *var;
+    StgMutClosure *mut_link;
 } StgMutVar;
 
 typedef struct _StgUpdateFrame {
@@ -251,8 +266,9 @@ typedef struct {
 
 typedef struct {
   StgHeader       header;
-  struct StgTSO_* head;
-  struct StgTSO_* tail;
+  struct StgTSO_ *head;
+  StgMutClosure  *mut_link;
+  struct StgTSO_ *tail;
   StgClosure*     value;
 } StgMVar;
 
index 41a61df..2095026 100644 (file)
@@ -1,5 +1,5 @@
 /* ----------------------------------------------------------------------------
- * $Id: InfoTables.h,v 1.2 1998/12/02 13:21:10 simonm Exp $
+ * $Id: InfoTables.h,v 1.3 1999/01/13 17:25:53 simonm Exp $
  * 
  * Info Tables
  *
@@ -130,7 +130,6 @@ typedef enum {
     , MVAR
 
     , ARR_WORDS
-    , ARR_PTRS
 
     , MUT_ARR_WORDS
     , MUT_ARR_PTRS
@@ -162,9 +161,12 @@ typedef enum {
 #define _UPT (1<<6)  /* unpointed?         */
 #define _SRT (1<<7)  /* has an SRT?        */
 
-#define isSTATIC(flags) ((flags)&_STA)
+#define isSTATIC(flags)  ((flags)&_STA)
+#define isMUTABLE(flags) ((flags) &_MUT)
+
 #define closure_STATIC(closure)       (  get_itbl(closure)->flags & _STA)
 #define closure_SHOULD_SPARK(closure) (!(get_itbl(closure)->flags & _NS))
+#define closure_MUTABLE(closure)      (  get_itbl(closure)->flags & _MUT)
 #define closure_UNPOINTED(closure)    (  get_itbl(closure)->flags & _UPT)
 
 /*                                 HNF  BTM   NS  STA  THU MUT UPT SRT */
@@ -191,58 +193,65 @@ typedef enum {
 #define FLAGS_EVACUATED                   0
 #define FLAGS_ARR_WORDS                   (_HNF|     _NS|              _UPT     )      
 #define FLAGS_MUT_ARR_WORDS       (_HNF|     _NS|         _MUT|_UPT     )      
-#define FLAGS_ARR_PTRS            (_HNF|     _NS|              _UPT     )      
 #define FLAGS_MUT_ARR_PTRS        (_HNF|     _NS|         _MUT|_UPT     )      
 #define FLAGS_MUT_ARR_PTRS_FROZEN  (_HNF|     _NS|         _MUT|_UPT     )     
 #define FLAGS_MUT_VAR             (_HNF|     _NS|         _MUT|_UPT     )      
 #define FLAGS_FOREIGN             (_HNF|     _NS|              _UPT     )      
 #define FLAGS_WEAK                (_HNF|     _NS|              _UPT     )      
-#define FLAGS_BLACKHOLE                   (     _BTM|_NS|              _UPT     )      
-#define FLAGS_MVAR                (_HNF|     _NS|              _UPT     )      
+#define FLAGS_BLACKHOLE                   (          _NS|              _UPT     )      
+#define FLAGS_MVAR                (_HNF|     _NS|         _MUT|_UPT     )      
 #define FLAGS_FETCH_ME            (_HNF|     _NS                        )      
-#define FLAGS_TSO                  0                               
+#define FLAGS_TSO                  (_HNF|     _NS|         _MUT|_UPT     )
 #define FLAGS_RET_BCO             (     _BTM                            )
 #define FLAGS_RET_SMALL                   (     _BTM|                       _SRT)
 #define FLAGS_RET_VEC_SMALL       (     _BTM|                       _SRT)
 #define FLAGS_RET_BIG             (                                 _SRT)
 #define FLAGS_RET_VEC_BIG         (                                 _SRT)
 #define FLAGS_RET_DYN             (                                 _SRT)
-#define FLAGS_CATCH_FRAME         0
-#define FLAGS_STOP_FRAME          0
-#define FLAGS_SEQ_FRAME           0
-#define FLAGS_UPDATE_FRAME         0
+#define FLAGS_CATCH_FRAME         (     _BTM                            )
+#define FLAGS_STOP_FRAME          (     _BTM                            )
+#define FLAGS_SEQ_FRAME           (     _BTM                            )
+#define FLAGS_UPDATE_FRAME         (     _BTM                            )
 
 /* -----------------------------------------------------------------------------
    Info Tables
    -------------------------------------------------------------------------- */
 
 /* A large bitmap.  Small 32-bit ones live in the info table, but sometimes
- * 32 bits isn't enough and we have to generate a larger one.
+ * 32 bits isn't enough and we have to generate a larger one.  (sizes
+ * differ for 64-bit machines.
  */
 
 typedef struct {
-  StgNat32 size;
-  StgNat32 bitmap[0];
+  StgWord size;
+  StgWord bitmap[0];
 } StgLargeBitmap;
 
 /*
  * Stuff describing the closure layout.  Well, actually, it might
- * contain the selector index for a THUNK_SELECTOR.
+ * contain the selector index for a THUNK_SELECTOR.  If we're on a
+ * 64-bit architecture then we can enlarge some of these fields, since
+ * the union contains a pointer field.
  */
 
 typedef union {
 
-  StgNat32 bitmap;             /* bit pattern, 1 = pointer, 0 = non-pointer */
-
+  StgWord bitmap;              /* bit pattern, 1 = pointer, 0 = non-pointer */
+  StgWord selector_offset;     /* used in THUNK_SELECTORs */
   StgLargeBitmap* large_bitmap;        /* pointer to large bitmap structure */
 
+#if SIZEOF_VOID_P == 8
+  struct {
+    StgNat32 ptrs;             /* number of pointers     */
+    StgNat32 nptrs;            /* number of non-pointers */
+  } payload;
+#else
   struct {
     StgNat16 ptrs;             /* number of pointers     */
     StgNat16 nptrs;            /* number of non-pointers */
   } payload;
+#endif
   
-  StgNat32 selector_offset;    /* used in THUNK_SELECTORs */
-
 } StgClosureInfo;
 
 /*
@@ -259,10 +268,16 @@ typedef struct _StgInfoTable {
     StgParInfo     par;
     StgProfInfo     prof;
     StgDebugInfo    debug;
-    StgClosureInfo  layout;    /* closure layout info */
+    StgClosureInfo  layout;    /* closure layout info (pointer-sized) */
+#if SIZEOF_VOID_P == 8
+    StgNat16        flags;     /* }                                   */
+    StgClosureType  type : 16; /* } These 4 elements fit into 64 bits */
+    StgNat32        srt_len;    /* }                                   */
+#else
     StgNat8         flags;     /* }                                   */
     StgClosureType  type : 8;  /* } These 4 elements fit into 32 bits */
     StgNat16        srt_len;    /* }                                   */
+#endif
 #if USE_MINIINTERPRETER
     StgFunPtr       (*vector)[];
     StgFunPtr       entry;
index ef1a19f..f16af65 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: PrimOps.h,v 1.2 1998/12/02 13:21:18 simonm Exp $
+ * $Id: PrimOps.h,v 1.3 1999/01/13 17:25:53 simonm Exp $
  *
  * Macros for primitive operations in STG-ish C code.
  *
@@ -109,6 +109,8 @@ typedef union {
   c = z.i[C];                                  \
 }
 
+
+
 #define subWithCarryZh(r,c,a,b)                        \
 { long_long_u z;                               \
   z.l = a + b;                                 \
@@ -407,25 +409,22 @@ LI_ stg_word64ToInt64 (StgNat64);
  * about increasing the alignment requirements.
  */
 #define REAL_BYTE_ARR_CTS(a)   ((void *) (((StgArrWords *)(a))->payload))
-#define REAL_PTRS_ARR_CTS(a)   ((P_)   (((StgArrPtrs  *)(a))->payload))
+#define REAL_PTRS_ARR_CTS(a)   ((P_)   (((StgMutArrPtrs  *)(a))->payload))
 
 #ifdef DEBUG
 #define BYTE_ARR_CTS(a)                                \
  ({ ASSERT(GET_INFO(a) == &ARR_WORDS_info);    \
     REAL_BYTE_ARR_CTS(a); })
 #define PTRS_ARR_CTS(a)                                \
- ({ ASSERT((GET_INFO(a) == &ARR_PTRS_info)     \
-       || (GET_INFO(a) == &MUT_ARR_PTRS_info));\
+ ({ ASSERT((GET_INFO(a) == &MUT_ARR_PTRS_info));\
     REAL_PTRS_ARR_CTS(a); })
 #else
 #define BYTE_ARR_CTS(a)                REAL_BYTE_ARR_CTS(a)
 #define PTRS_ARR_CTS(a)                REAL_PTRS_ARR_CTS(a)
 #endif
 
-/* Todo: define... */
 extern I_ genSymZh(void);
 extern I_ resetGenSymZh(void);
-extern I_ incSeqWorldZh(void);
 
 /*--- everything except new*Array is done inline: */
 
index c8dcaae..3f7d868 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Rts.h,v 1.2 1998/12/02 13:21:21 simonm Exp $
+ * $Id: Rts.h,v 1.3 1999/01/13 17:25:54 simonm Exp $
  *
  * Top-level include file for the RTS itself
  *
index c0bde3b..2ef0534 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: StgMiscClosures.h,v 1.2 1998/12/02 13:21:39 simonm Exp $
+ * $Id: StgMiscClosures.h,v 1.3 1999/01/13 17:25:54 simonm Exp $
  *
  * Entry code for various built-in closure types.
  *
@@ -31,11 +31,12 @@ STGFUN(FULL_MVAR_entry);
 STGFUN(EMPTY_MVAR_entry);
 STGFUN(ARR_WORDS_entry);
 STGFUN(MUT_ARR_WORDS_entry);
-STGFUN(ARR_PTRS_entry);
 STGFUN(MUT_ARR_PTRS_entry);
 STGFUN(MUT_ARR_PTRS_FROZEN_entry);
 STGFUN(MUT_VAR_entry);
 STGFUN(END_TSO_QUEUE_entry);
+STGFUN(MUT_CONS_entry);
+STGFUN(END_MUT_LIST_entry);
 STGFUN(dummy_ret_entry);
 
 /* info tables */
@@ -59,11 +60,12 @@ extern const StgInfoTable EMPTY_MVAR_info;
 extern const StgInfoTable TSO_info;
 extern const StgInfoTable ARR_WORDS_info;
 extern const StgInfoTable MUT_ARR_WORDS_info;
-extern const StgInfoTable ARR_PTRS_info;
 extern const StgInfoTable MUT_ARR_PTRS_info;
 extern const StgInfoTable MUT_ARR_PTRS_FROZEN_info;
 extern const StgInfoTable MUT_VAR_info;
 extern const StgInfoTable END_TSO_QUEUE_info;
+extern const StgInfoTable MUT_CONS_info;
+extern const StgInfoTable END_MUT_LIST_info;
 extern const StgInfoTable catch_info;
 extern const StgInfoTable seq_info;
 extern const StgInfoTable dummy_ret_info;
@@ -78,6 +80,7 @@ extern const StgInfoTable ret_bco_info;
 /* closures */
 
 extern const StgClosure END_TSO_QUEUE_closure;
+extern const StgClosure END_MUT_LIST_closure;
 extern const StgClosure dummy_ret_closure;
 
 extern StgIntCharlikeClosure CHARLIKE_closure[];
index 6167b31..c5f53c4 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: TSO.h,v 1.2 1998/12/02 13:21:43 simonm Exp $
+ * $Id: TSO.h,v 1.3 1999/01/13 17:25:55 simonm Exp $
  *
  * The definitions for Thread State Objects.
  *
@@ -52,7 +52,7 @@ typedef enum {
  * even doing 10^6 forks per second would take 35 million years to
  * overflow a 64 bit thread ID :-)
  */
-typedef StgNat64 StgThreadID;
+typedef StgNat32 StgThreadID;
 
 /*
  * This type is returned to the scheduler by a thread that has
@@ -76,6 +76,7 @@ typedef enum {
 typedef struct StgTSO_ {
   StgHeader          header;
   struct StgTSO_*    link;
+  StgMutClosure *    mut_link; /* TSO's are mutable of course! */
   StgTSOWhatNext     whatNext;
   StgTSOState        state;    /* necessary? */
   StgThreadID        id;
index 9209739..3a599c2 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Updates.h,v 1.2 1998/12/02 13:21:47 simonm Exp $
+ * $Id: Updates.h,v 1.3 1999/01/13 17:25:55 simonm Exp $
  *
  * Definitions related to updates.
  *
@@ -8,18 +8,6 @@
 #ifndef UPDATES_H
 #define UPDATES_H
 
-/*
-  ticky-ticky wants to use permanent indirections when it's doing
-  update entry counts.
- */
-
-#ifndef TICKY_TICKY
-# define Ind_info_TO_USE &IND_info
-#else
-# define Ind_info_TO_USE ((AllFlags.doUpdEntryCounts) ? &IND_PERM_info : &IND_info
-)
-#endif
-
 /* -----------------------------------------------------------------------------
    Update a closure with an indirection.  This may also involve waking
    up a queue of blocked threads waiting on the result of this
  *       (I think the fancy version of the GC is supposed to do this too.)
  */
 
+/* This expands to a fair chunk of code, what with waking up threads 
+ * and checking whether we're updating something in a old generation.
+ * preferably don't use this macro inline in compiled code.
+ */
+
 #define UPD_IND(updclosure, heapptr)                            \
         TICK_UPDATED_SET_UPDATED(updclosure);                  \
         AWAKEN_BQ(updclosure);                                  \
-        SET_INFO((StgInd*)updclosure,Ind_info_TO_USE);          \
-        ((StgInd *)updclosure)->indirectee   = (StgClosure *)(heapptr)
+       updateWithIndirection((StgClosure *)updclosure,         \
+                             (StgClosure *)heapptr);
 
 /* -----------------------------------------------------------------------------
    Update a closure inplace with an infotable that expects 1 (closure)
@@ -105,11 +98,11 @@ extern const StgPolyInfoTable Upd_frame_info;
 
        - for the parallel system, which can implement updates more
          easily if the updatee is always in the heap. (allegedly).
+
+   When debugging, we maintain a separate CAF list so we can tell when
+   a CAF has been garbage collected.
    -------------------------------------------------------------------------- */
    
-EI_(Caf_info);
-EF_(Caf_entry);
-
 /* ToDo: only call newCAF when debugging. */
 
 extern void newCAF(StgClosure*);
index e0ded8e..26f2a60 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: BlockAlloc.c,v 1.2 1998/12/02 13:28:12 simonm Exp $
+ * $Id: BlockAlloc.c,v 1.3 1999/01/13 17:25:37 simonm Exp $
  *
  * The block allocator and free list manager.
  *
@@ -210,6 +210,14 @@ freeGroup(bdescr *p)
     return;
   }
 
+#ifdef DEBUG
+  p->free = (void *)-1;  /* indicates that this block is free */
+  p->step = NULL;
+  p->gen  = NULL;
+  /* fill the block group with garbage if sanity checking is on */
+  IF_DEBUG(sanity,memset(p->start, 0xaa, p->blocks * BLOCK_SIZE));
+#endif
+
   /* find correct place in free list to place new group */
   last = NULL;
   for (bd = free_list; bd != NULL && bd->start < p->start; 
@@ -252,9 +260,6 @@ freeChain(bdescr *bd)
   bdescr *next_bd;
   while (bd != NULL) {
     next_bd = bd->link;
-#ifdef DEBUG
-    bd->free = (void *)-1;  /* indicates that this block is free */
-#endif
     freeGroup(bd);
     bd = next_bd;
   }
@@ -301,4 +306,16 @@ checkFreeListSanity(void)
     }
   }
 }
+
+nat /* BLOCKS */
+countFreeList(void)
+{
+  bdescr *bd;
+  lnat total_blocks = 0;
+
+  for (bd = free_list; bd != NULL; bd = bd->link) {
+    total_blocks += bd->blocks;
+  }
+  return total_blocks;
+}
 #endif
index d3e6d53..1ef18d4 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: BlockAlloc.h,v 1.2 1998/12/02 13:28:13 simonm Exp $
+ * $Id: BlockAlloc.h,v 1.3 1999/01/13 17:25:38 simonm Exp $
  *
  * Block Allocator Interface
  *
@@ -36,6 +36,7 @@ static inline bdescr *Bdescr(StgPtr p)
 
 #ifdef DEBUG
 extern void checkFreeListSanity(void);
+nat         countFreeList(void);
 #endif
 
 #endif BLOCK_ALLOC_H
index 662ad41..7fe57ca 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: DebugProf.c,v 1.2 1998/12/02 13:28:14 simonm Exp $
+ * $Id: DebugProf.c,v 1.3 1999/01/13 17:25:38 simonm Exp $
  *
  * (c) The GHC Team 1998
  *
@@ -160,7 +160,6 @@ static char *type_names[] = {
     , "MVAR"
 
     , "ARR_WORDS"
-    , "ARR_PTRS"
 
     , "MUT_ARR_WORDS"
     , "MUT_ARR_PTRS"
@@ -316,10 +315,9 @@ heapCensus(bdescr *bd)
                size = arr_words_sizeW(stgCast(StgArrWords*,p));
                break;
 
-           case ARR_PTRS:
            case MUT_ARR_PTRS:
            case MUT_ARR_PTRS_FROZEN:
-               size = arr_ptrs_sizeW((StgArrPtrs *)p);
+               size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
                break;
 
            case TSO:
index 23b83a5..0a434b1 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.5 1999/01/06 12:27:47 simonm Exp $
+ * $Id: GC.c,v 1.6 1999/01/13 17:25:39 simonm Exp $
  *
  * Two-space garbage collector
  *
 
 StgCAF* enteredCAFs;
 
-static P_ toHp;                        /* to-space heap pointer */
-static P_ toHpLim;             /* end of current to-space block */
-static bdescr *toHp_bd;                /* descriptor of current to-space block  */
-static nat blocks = 0;         /* number of to-space blocks allocated */
-static bdescr *old_to_space = NULL; /* to-space from the last GC */
-static nat old_to_space_blocks = 0; /* size of previous to-space */
-
 /* STATIC OBJECT LIST.
  *
+ * During GC:
  * We maintain a linked list of static objects that are still live.
  * The requirements for this list are:
  *
@@ -53,20 +47,42 @@ static nat old_to_space_blocks = 0; /* size of previous to-space */
  *
  * An object is on the list if its static link field is non-zero; this
  * means that we have to mark the end of the list with '1', not NULL.  
+ *
+ * Extra notes for generational GC:
+ *
+ * Each generation has a static object list associated with it.  When
+ * collecting generations up to N, we treat the static object lists
+ * from generations > N as roots.
+ *
+ * We build up a static object list while collecting generations 0..N,
+ * which is then appended to the static object list of generation N+1.
+ */
+StgClosure* static_objects;          /* live static objects */
+StgClosure* scavenged_static_objects; /* static objects scavenged so far */
+
+/* N is the oldest generation being collected, where the generations
+ * are numbered starting at 0.  A major GC (indicated by the major_gc
+ * flag) is when we're collecting all generations.  We only attempt to
+ * deal with static objects and GC CAFs when doing a major GC.
+ */
+static nat N;
+static rtsBool major_gc;
+
+/* Youngest generation that objects should be evacuated to in
+ * evacuate().  (Logically an argument to evacuate, but it's static
+ * a lot of the time so we optimise it into a global variable).
  */
-#define END_OF_STATIC_LIST stgCast(StgClosure*,1)
-static StgClosure* static_objects;
-static StgClosure* scavenged_static_objects;
+static nat evac_gen;
 
 /* WEAK POINTERS
  */
 static StgWeak *old_weak_ptr_list; /* also pending finaliser list */
 static rtsBool weak_done;      /* all done for this pass */
 
-/* LARGE OBJECTS.
+/* Flag indicating failure to evacuate an object to the desired
+ * generation.
  */
-static bdescr *new_large_objects; /* large objects evacuated so far */
-static bdescr *scavenged_large_objects; /* large objects scavenged */
+static rtsBool failed_to_evac;
 
 /* -----------------------------------------------------------------------------
    Static function declarations
@@ -74,13 +90,16 @@ static bdescr *scavenged_large_objects; /* large objects scavenged */
 
 static StgClosure *evacuate(StgClosure *q);
 static void    zeroStaticObjectList(StgClosure* first_static);
-static void    scavenge_stack(StgPtr p, StgPtr stack_end);
-static void    scavenge_static(void);
-static void    scavenge_large(void);
-static StgPtr  scavenge(StgPtr to_scan);
 static rtsBool traverse_weak_ptr_list(void);
+static void    zeroMutableList(StgMutClosure *first);
 static void    revertDeadCAFs(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 StgMutClosure *scavenge_mutable_list(StgMutClosure *p, nat gen);
+
 #ifdef DEBUG
 static void gcCAFs(void);
 #endif
@@ -88,16 +107,33 @@ static void gcCAFs(void);
 /* -----------------------------------------------------------------------------
    GarbageCollect
 
-   This function performs a full copying garbage collection.
+   For garbage collecting generation N (and all younger generations):
+
+     - follow all pointers in the root set.  the root set includes all 
+       mutable objects in all steps in all generations.
+
+     - for each pointer, evacuate the object it points to into either
+       + to-space in the next higher step in that generation, if one exists,
+       + if the object's generation == N, then evacuate it to the next
+         generation if one exists, or else to-space in the current
+        generation.
+       + if the object's generation < N, then evacuate it to to-space
+         in the next generation.
+
+     - repeatedly scavenge to-space from each step in each generation
+       being collected until no more objects can be evacuated.
+      
+     - free from-space in each step, and set from-space = to-space.
+
    -------------------------------------------------------------------------- */
 
 void GarbageCollect(void (*get_roots)(void))
 {
-  bdescr *bd, *scan_bd, *to_space;
-  StgPtr scan;
-  lnat allocated, live;
-  nat old_nursery_blocks = nursery_blocks;       /* for stats */
-  nat old_live_blocks    = old_to_space_blocks;  /* ditto */
+  bdescr *bd;
+  step *step;
+  lnat live, allocated, collected = 0;
+  nat g, s;
+
 #ifdef PROFILING
   CostCentreStack *prev_CCS;
 #endif
@@ -115,8 +151,7 @@ void GarbageCollect(void (*get_roots)(void))
    * which case we need to call threadPaused() because the scheduler
    * won't have done it.
    */
-  if (CurrentTSO) 
-    threadPaused(CurrentTSO);
+  if (CurrentTSO) { threadPaused(CurrentTSO); }
 
   /* Approximate how much we allocated: number of blocks in the
    * nursery + blocks allocated via allocate() - unused nusery blocks.
@@ -127,34 +162,138 @@ void GarbageCollect(void (*get_roots)(void))
   for ( bd = current_nursery->link; bd != NULL; bd = bd->link ) {
     allocated -= BLOCK_SIZE_W;
   }
-  
+
+  /* Figure out which generation to collect
+   */
+  for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+    if (generations[g].steps[0].n_blocks >= generations[g].max_blocks) {
+      N = g;
+    }
+  }
+  major_gc = (N == RtsFlags.GcFlags.generations-1);
+
   /* check stack sanity *before* GC (ToDo: check all threads) */
   /*IF_DEBUG(sanity, checkTSO(MainTSO,0)); */
   IF_DEBUG(sanity, checkFreeListSanity());
 
+  /* Initialise the static object lists
+   */
   static_objects = END_OF_STATIC_LIST;
   scavenged_static_objects = END_OF_STATIC_LIST;
 
-  new_large_objects = NULL;
-  scavenged_large_objects = NULL;
+  /* zero the mutable list for the oldest generation (see comment by
+   * zeroMutableList below).
+   */
+  if (major_gc) { 
+    zeroMutableList(generations[RtsFlags.GcFlags.generations-1].mut_list);
+  }
+
+  /* Initialise to-space in all the generations/steps that we're
+   * collecting.
+   */
+  for (g = 0; g <= N; g++) {
+    generations[g].mut_list = END_MUT_LIST;
+
+    for (s = 0; s < generations[g].n_steps; s++) {
+      /* generation 0, step 0 doesn't need to-space */
+      if (g == 0 && s == 0) { continue; }
+      /* Get a free block for to-space.  Extra blocks will be chained on
+       * as necessary.
+       */
+      bd = allocBlock();
+      step = &generations[g].steps[s];
+      ASSERT(step->gen->no == g);
+      ASSERT(step->hp ? Bdescr(step->hp)->step == step : rtsTrue);
+      bd->gen  = &generations[g];
+      bd->step = step;
+      bd->link = NULL;
+      bd->evacuated = 1;       /* it's a to-space block */
+      step->hp        = bd->start;
+      step->hpLim     = step->hp + BLOCK_SIZE_W;
+      step->hp_bd     = bd;
+      step->to_space  = bd;
+      step->to_blocks = 1; /* ???? */
+      step->scan      = bd->start;
+      step->scan_bd   = bd;
+      step->new_large_objects = NULL;
+      step->scavenged_large_objects = NULL;
+      /* mark the large objects as not evacuated yet */
+      for (bd = step->large_objects; bd; bd = bd->link) {
+       bd->evacuated = 0;
+      }
+    }
+  }
+
+  /* make sure the older generations have at least one block to
+   * allocate into (this makes things easier for copy(), see below.
+   */
+  for (g = N+1; g < RtsFlags.GcFlags.generations; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      step = &generations[g].steps[s];
+      if (step->hp_bd == NULL) {
+       bd = allocBlock();
+       bd->gen = &generations[g];
+       bd->step = step;
+       bd->link = NULL;
+       bd->evacuated = 0;      /* *not* a to-space block */
+       step->hp = bd->start;
+       step->hpLim = step->hp + BLOCK_SIZE_W;
+       step->hp_bd = bd;
+       step->blocks = bd;
+       step->n_blocks = 1;
+      }
+      /* Set the scan pointer for older generations: remember we
+       * still have to scavenge objects that have been promoted. */
+      step->scan = step->hp;
+      step->scan_bd = step->hp_bd;
+      step->to_space = NULL;
+      step->to_blocks = 0;
+      step->new_large_objects = NULL;
+      step->scavenged_large_objects = NULL;
+#ifdef DEBUG
+      /* retain these so we can sanity-check later on */
+      step->old_scan    = step->scan;
+      step->old_scan_bd = step->scan_bd;
+#endif
+    }
+  }
 
-  /* Get a free block for to-space.  Extra blocks will be chained on
-   * as necessary.
+  /* -----------------------------------------------------------------------
+   * follow all the roots that the application knows about.
    */
-  bd = allocBlock();
-  bd->step = 1;                        /* step 1 identifies to-space */
-  toHp = bd->start;
-  toHpLim = toHp + BLOCK_SIZE_W;
-  toHp_bd = bd;
-  to_space = bd;
-  blocks = 0;
-
-  scan = toHp;
-  scan_bd = bd;
-
-  /* follow all the roots that the application knows about */
+  evac_gen = 0;
   get_roots();
 
+  /* follow all the roots that we know about:
+   *   - mutable lists from each generation > N
+   * we want to *scavenge* these roots, not evacuate them: they're not
+   * going to move in this GC.
+   * Also: do them in reverse generation order.  This is because we
+   * often want to promote objects that are pointed to by older
+   * generations early, so we don't have to repeatedly copy them.
+   * Doing the generations in reverse order ensures that we don't end
+   * up in the situation where we want to evac an object to gen 3 and
+   * it has already been evaced to gen 2.
+   */
+  { 
+    StgMutClosure *tmp, **pp;
+    for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
+      /* the act of scavenging the mutable list for this generation
+       * might place more objects on the mutable list itself.  So we
+       * place the current mutable list in a temporary, scavenge it,
+       * and then append it to the new list.
+       */
+      tmp = generations[g].mut_list;
+      generations[g].mut_list = END_MUT_LIST;
+      tmp = scavenge_mutable_list(tmp, g);
+
+      pp = &generations[g].mut_list;
+      while (*pp != END_MUT_LIST) {
+          pp = &(*pp)->mut_link;
+      }
+      *pp = tmp;
+    }
+  }  
   /* And don't forget to mark the TSO if we got here direct from
    * Haskell! */
   if (CurrentTSO) {
@@ -195,190 +334,225 @@ void GarbageCollect(void (*get_roots)(void))
   }
 #endif
 
-  /* Then scavenge all the objects we picked up on the first pass. 
-   * We may require multiple passes to find all the static objects,
-   * large objects and normal objects.
+  /* -------------------------------------------------------------------------
+   * Repeatedly scavenge all the areas we know about until there's no
+   * more scavenging to be done.
    */
   { 
+    rtsBool flag;
   loop:
-    if (static_objects != END_OF_STATIC_LIST) {
+    flag = rtsFalse;
+
+    /* scavenge static objects */
+    if (major_gc && static_objects != END_OF_STATIC_LIST) {
       scavenge_static();
     }
-    if (toHp_bd != scan_bd || scan < toHp) {
-      scan = scavenge(scan);
-      scan_bd = Bdescr(scan);
-      goto loop;
-    }
-    if (new_large_objects != NULL) {
-      scavenge_large();
-      goto loop;
+
+    /* When scavenging the older generations:  Objects may have been
+     * evacuated from generations <= N into older generations, and we
+     * need to scavenge these objects.  We're going to try to ensure that
+     * any evacuations that occur move the objects into at least the
+     * same generation as the object being scavenged, otherwise we
+     * have to create new entries on the mutable list for the older
+     * generation.
+     */
+
+    /* scavenge each step in generations 0..maxgen */
+    { 
+      int gen; 
+      for (gen = RtsFlags.GcFlags.generations-1; gen >= 0; gen--) {
+       for (s = 0; s < generations[gen].n_steps; s++) {
+         step = &generations[gen].steps[s];
+         evac_gen = gen;
+         if (step->hp_bd != step->scan_bd || step->scan < step->hp) {
+           scavenge(step);
+           flag = rtsTrue;
+         }
+         if (step->new_large_objects != NULL) {
+           scavenge_large(step);
+           flag = rtsTrue;
+         }
+       }
+      }
     }
+    if (flag) { goto loop; }
+
     /* must be last... */
     if (traverse_weak_ptr_list()) { /* returns rtsTrue if evaced something */
       goto loop;
     }
   }
 
-  /* tidy up the end of the to-space chain */
-  toHp_bd->free = toHp;
-  toHp_bd->link = NULL;
+  /* run through all the generations/steps and tidy up 
+   */
+  for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      bdescr *next;
+      step = &generations[g].steps[s];
+
+      if (!(g == 0 && s == 0)) {
+       /* Tidy the end of the to-space chains */
+       step->hp_bd->free = step->hp;
+       step->hp_bd->link = NULL;
+      }
+
+      /* for generations we collected... */
+      if (g <= N) {
+
+       generations[g].collections++; /* for stats */
+       collected += step->n_blocks * BLOCK_SIZE_W; /* for stats */
+
+       /* free old memory and shift to-space into from-space for all
+        * the collected steps (except the allocation area).  These
+        * freed blocks will probaby be quickly recycled.
+        */
+       if (!(g == 0 && s == 0)) {
+         freeChain(step->blocks);
+         step->blocks = step->to_space;
+         step->n_blocks = step->to_blocks;
+         step->to_space = NULL;
+         step->to_blocks = 0;
+         for (bd = step->blocks; bd != NULL; bd = bd->link) {
+           bd->evacuated = 0;  /* now from-space */
+         }
+       }
+
+       /* LARGE OBJECTS.  The current live large objects are chained on
+        * scavenged_large, having been moved during garbage
+        * collection from large_objects.  Any objects left on
+        * large_objects list are therefore dead, so we free them here.
+        */
+       for (bd = step->large_objects; bd != NULL; bd = next) {
+         next = bd->link;
+         freeGroup(bd);
+         bd = next;
+       }
+       for (bd = step->scavenged_large_objects; bd != NULL; bd = bd->link) {
+         bd->evacuated = 0;
+       }
+       step->large_objects = step->scavenged_large_objects;
+
+       /* Set the maximum blocks for this generation,
+        * using an arbitrary factor of the no. of blocks in step 0.
+        */
+       if (g != 0) {
+         generation *gen = &generations[g];
+         gen->max_blocks = 
+           stg_max(gen->steps[s].n_blocks * 2,
+                   RtsFlags.GcFlags.minAllocAreaSize * 4);
+         if (gen->max_blocks > RtsFlags.GcFlags.maxHeapSize / 2) {
+           gen->max_blocks = RtsFlags.GcFlags.maxHeapSize / 2;
+           if (((int)gen->max_blocks - (int)gen->steps[0].n_blocks) < 
+               (RtsFlags.GcFlags.pcFreeHeap *
+                RtsFlags.GcFlags.maxHeapSize / 200)) {
+             heapOverflow();
+           }
+         }
+       }
+       
+      /* for older generations... */
+      } else {
+       
+       /* For older generations, we need to append the
+        * scavenged_large_object list (i.e. large objects that have been
+        * promoted during this GC) to the large_object list for that step.
+        */
+       for (bd = step->scavenged_large_objects; bd; bd = next) {
+         next = bd->link;
+         bd->evacuated = 0;
+         dbl_link_onto(bd, &step->large_objects);
+       }
+
+       /* add the new blocks we promoted during this GC */
+       step->n_blocks += step->to_blocks;
+      }
+    }
+  }
   
   /* revert dead CAFs and update enteredCAFs list */
   revertDeadCAFs();
   
   /* mark the garbage collected CAFs as dead */
 #ifdef DEBUG
-  gcCAFs();
+  if (major_gc) { gcCAFs(); }
 #endif
   
-  zeroStaticObjectList(scavenged_static_objects);
-  
-  /* approximate amount of live data (doesn't take into account slop
-   * at end of each block).  ToDo: this more accurately.
-   */
-  live = blocks * BLOCK_SIZE_W + ((lnat)toHp_bd->free -
-                                 (lnat)toHp_bd->start) / sizeof(W_);
+  /* zero the scavenged static object list */
+  if (major_gc) {
+    zeroStaticObjectList(scavenged_static_objects);
+  }
 
-  /* Free the to-space from the last GC, as it has now been collected.
-   * we may be able to re-use these blocks in creating a new nursery,
-   * below.  If not, the blocks will probably be re-used for to-space
-   * in the next GC.
+  /* Reset the nursery
    */
-  if (old_to_space != NULL) {
-    freeChain(old_to_space);
+  for (bd = g0s0->blocks; bd; bd = bd->link) {
+    bd->free = bd->start;
+    ASSERT(bd->gen == g0);
+    ASSERT(bd->step == g0s0);
+  }
+  current_nursery = g0s0->blocks;
+
+  live = 0;
+  for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      /* approximate amount of live data (doesn't take into account slop
+       * at end of each block).  ToDo: this more accurately.
+       */
+      if (g == 0 && s == 0) { continue; }
+      step = &generations[g].steps[s];
+      live += step->n_blocks * BLOCK_SIZE_W + 
+       ((lnat)step->hp_bd->free -(lnat)step->hp_bd->start) / sizeof(W_);
+    }
   }
-  old_to_space = to_space;
-  old_to_space_blocks = blocks;
 
   /* Free the small objects allocated via allocate(), since this will
-   * all have been copied into to-space now.  
+   * 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 = stg_max(blocks,RtsFlags.GcFlags.minAllocAreaSize);
-
-  /* LARGE OBJECTS.  The current live large objects are chained on
-   * scavenged_large_objects, having been moved during garbage
-   * collection from large_alloc_list.  Any objects left on
-   * large_alloc list are therefore dead, so we free them here.
-   */
-  {
-    bdescr *bd, *next;
-    bd = large_alloc_list;
-    while (bd != NULL) {
-      next = bd->link;
-      freeGroup(bd);
-      bd = next;
-    }
-    large_alloc_list = scavenged_large_objects;
-  }
-
+  alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
 
+  /* start any pending finalisers */
+  scheduleFinalisers(old_weak_ptr_list);
+  
   /* check sanity after GC */
-  IF_DEBUG(sanity, checkHeap(to_space,1));
-  /*IF_DEBUG(sanity, checkTSO(MainTSO,1)); */
-  IF_DEBUG(sanity, checkFreeListSanity());
-
 #ifdef DEBUG
-  /* symbol-table based profiling */
-  heapCensus(to_space);
-#endif
-
-  /* set up a new nursery.  Allocate a nursery size based on a
-   * function of the amount of live data (currently a factor of 2,
-   * should be configurable (ToDo)).  Use the blocks from the old
-   * nursery if possible, freeing up any left over blocks.
-   *
-   * If we get near the maximum heap size, then adjust our nursery
-   * size accordingly.  If the nursery is the same size as the live
-   * data (L), then we need 3L bytes.  We can reduce the size of the
-   * nursery to bring the required memory down near 2L bytes.
-   * 
-   * A normal 2-space collector would need 4L bytes to give the same
-   * performance we get from 3L bytes, reducing to the same
-   * performance at 2L bytes.  
-   */
-  if ( blocks * 4 > RtsFlags.GcFlags.maxHeapSize ) {
-    int adjusted_blocks;  /* signed on purpose */
-    int pc_free; 
-
-    adjusted_blocks = (RtsFlags.GcFlags.maxHeapSize - 2 * blocks);
-    IF_DEBUG(gc, fprintf(stderr, "Near maximum heap size of 0x%x blocks, blocks = %d, adjusted to %d\n", RtsFlags.GcFlags.maxHeapSize, blocks, adjusted_blocks));
-    pc_free = adjusted_blocks * 100 / RtsFlags.GcFlags.maxHeapSize;
-    if (pc_free < RtsFlags.GcFlags.pcFreeHeap) /* might even be < 0 */ {
-      heapOverflow();
-    }
-    blocks = adjusted_blocks;
-
-  } else {
-    blocks *= 2;
-    if (blocks < RtsFlags.GcFlags.minAllocAreaSize) {
-     blocks = RtsFlags.GcFlags.minAllocAreaSize;
+  for (g = 0; g <= N; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      if (g == 0 && s == 0) { continue; }
+      IF_DEBUG(sanity, checkHeap(generations[g].steps[s].blocks, NULL));
+      IF_DEBUG(sanity, checkChain(generations[g].steps[s].large_objects));
     }
   }
-  
-  if (nursery_blocks < blocks) {
-    IF_DEBUG(gc, fprintf(stderr, "Increasing size of nursery to %d blocks\n", 
-                        blocks));
-    nursery = allocNursery(nursery,blocks-nursery_blocks);
-  } else {
-    bdescr *next_bd = nursery;
-
-    IF_DEBUG(gc, fprintf(stderr, "Decreasing size of nursery to %d blocks\n", 
-                        blocks));
-    for (bd = nursery; nursery_blocks > blocks; nursery_blocks--) {
-      next_bd = bd->link;
-      freeGroup(bd);
-      bd = next_bd;
+  for (g = N+1; g < RtsFlags.GcFlags.generations; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      IF_DEBUG(sanity, checkHeap(generations[g].steps[s].old_scan_bd,
+                                generations[g].steps[s].old_scan));
+      IF_DEBUG(sanity, checkChain(generations[g].steps[s].large_objects));
     }
-    nursery = bd;
   }
-    
-  current_nursery = nursery;
-  nursery_blocks = blocks;
+  IF_DEBUG(sanity, checkFreeListSanity());
+#endif
 
-  /* set the step number for each block in the nursery to zero */
-  for (bd = nursery; bd != NULL; bd = bd->link) {
-    bd->step = 0;
-    bd->free = bd->start;
-  }
-  for (bd = to_space; bd != NULL; bd = bd->link) {
-    bd->step = 0;
-  }
-  for (bd = large_alloc_list; bd != NULL; bd = bd->link) {
-    bd->step = 0;
-  }
+  IF_DEBUG(gc, stat_describe_gens());
 
 #ifdef DEBUG
-  /* check that we really have the right number of blocks in the
-   * nursery, or things could really get screwed up.
-   */
-  {
-    nat i = 0;
-    for (bd = nursery; bd != NULL; bd = bd->link) {
-      ASSERT(bd->free == bd->start);
-      ASSERT(bd->step == 0);
-      i++;
-    }
-    ASSERT(i == nursery_blocks);
-  }
+  /* symbol-table based profiling */
+  /*  heapCensus(to_space); */ /* ToDo */
 #endif
 
-  /* start any pending finalisers */
-  scheduleFinalisers(old_weak_ptr_list);
-  
   /* restore enclosing cost centre */
 #ifdef PROFILING
   CCCS = prev_CCS;
 #endif
 
+  /* check for memory leaks if sanity checking is on */
+  IF_DEBUG(sanity, memInventory());
+
   /* ok, GC over: tell the stats department what happened. */
-  stat_endGC(allocated, 
-            (old_nursery_blocks + old_live_blocks) * BLOCK_SIZE_W,
-            live, "");
+  stat_endGC(allocated, collected, live, N);
 }
 
 /* -----------------------------------------------------------------------------
@@ -394,6 +568,11 @@ void GarbageCollect(void (*get_roots)(void))
    pointer code decide which weak pointers are dead - if there are no
    new live weak pointers, then all the currently unreachable ones are
    dead.
+
+   For generational GC: we just don't try to finalise weak pointers in
+   older generations than the one we're collecting.  This could
+   probably be optimised by keeping per-generation lists of weak
+   pointers, but for a few weak pointers this scheme will work.
    -------------------------------------------------------------------------- */
 
 static rtsBool 
@@ -406,17 +585,35 @@ traverse_weak_ptr_list(void)
 
   if (weak_done) { return rtsFalse; }
 
+  /* doesn't matter where we evacuate values/finalisers to, since
+   * these pointers are treated as roots (iff the keys are alive).
+   */
+  evac_gen = 0;
+
   last_w = &old_weak_ptr_list;
   for (w = old_weak_ptr_list; w; w = next_w) {
     target = w->key;
   loop:
+    /* ignore weak pointers in older generations */
+    if (!LOOKS_LIKE_STATIC(target) && Bdescr((P_)target)->gen->no > N) {
+      IF_DEBUG(weak, fprintf(stderr,"Weak pointer still alive (in old gen) at %p\n", w));
+      /* remove this weak ptr from the old_weak_ptr list */
+      *last_w = w->link;
+      /* and put it on the new weak ptr list */
+      next_w  = w->link;
+      w->link = weak_ptr_list;
+      weak_ptr_list = w;
+      flag = rtsTrue;
+      continue;
+    }
+
     info = get_itbl(target);
     switch (info->type) {
       
     case IND:
     case IND_STATIC:
     case IND_PERM:
-    case IND_OLDGEN:
+    case IND_OLDGEN:           /* rely on compatible layout with StgInd */
     case IND_OLDGEN_PERM:
       /* follow indirections */
       target = ((StgInd *)target)->indirectee;
@@ -463,36 +660,66 @@ traverse_weak_ptr_list(void)
   return rtsTrue;
 }
 
-StgClosure *MarkRoot(StgClosure *root)
+StgClosure *
+MarkRoot(StgClosure *root)
 {
   root = evacuate(root);
   return root;
 }
 
-static __inline__ StgClosure *copy(StgClosure *src, W_ size)
+static inline void addBlock(step *step)
+{
+  bdescr *bd = allocBlock();
+  bd->gen = step->gen;
+  bd->step = step;
+
+  if (step->gen->no <= N) {
+    bd->evacuated = 1;
+  } else {
+    bd->evacuated = 0;
+  }
+
+  step->hp_bd->free = step->hp;
+  step->hp_bd->link = bd;
+  step->hp = bd->start;
+  step->hpLim = step->hp + BLOCK_SIZE_W;
+  step->hp_bd = bd;
+  step->to_blocks++;
+}
+
+static __inline__ StgClosure *
+copy(StgClosure *src, W_ size, bdescr *bd)
 {
+  step *step;
   P_ to, from, dest;
 
-  if (toHp + size >= toHpLim) {
-    bdescr *bd = allocBlock();
-    toHp_bd->free = toHp;
-    toHp_bd->link = bd;
-    bd->step = 1;              /* step 1 identifies to-space */
-    toHp = bd->start;
-    toHpLim = toHp + BLOCK_SIZE_W;
-    toHp_bd = bd;
-    blocks++;
+  /* Find out where we're going, using the handy "to" pointer in 
+   * the step of the source object.  If it turns out we need to
+   * evacuate to an older generation, adjust it here (see comment
+   * by evacuate()).
+   */
+  step = bd->step->to;
+  if (step->gen->no < evac_gen) {
+    step = &generations[evac_gen].steps[0];
+  }
+
+  /* chain a new block onto the to-space for the destination step if
+   * necessary.
+   */
+  if (step->hp + size >= step->hpLim) {
+    addBlock(step);
   }
 
-  dest = toHp;
-  toHp += size;
+  dest = step->hp;
+  step->hp += size;
   for(to = dest, from = (P_)src; size>0; --size) {
     *to++ = *from++;
   }
   return (StgClosure *)dest;
 }
 
-static __inline__ void upd_evacuee(StgClosure *p, StgClosure *dest)
+static __inline__ void 
+upd_evacuee(StgClosure *p, StgClosure *dest)
 {
   StgEvacuated *q = (StgEvacuated *)p;
 
@@ -501,53 +728,166 @@ static __inline__ void upd_evacuee(StgClosure *p, StgClosure *dest)
 }
 
 /* -----------------------------------------------------------------------------
+   Evacuate a mutable object
+   
+   If we evacuate a mutable object to an old generation, cons the
+   object onto the older generation's mutable list.
+   -------------------------------------------------------------------------- */
+   
+static inline void
+evacuate_mutable(StgMutClosure *c)
+{
+  bdescr *bd;
+  
+  bd = Bdescr((P_)c);
+  if (bd->gen->no > 0) {
+    c->mut_link = bd->gen->mut_list;
+    bd->gen->mut_list = c;
+  }
+}
+
+/* -----------------------------------------------------------------------------
    Evacuate a large object
 
    This just consists of removing the object from the (doubly-linked)
    large_alloc_list, and linking it on to the (singly-linked)
    new_large_objects list, from where it will be scavenged later.
+
+   Convention: bd->evacuated is /= 0 for a large object that has been
+   evacuated, or 0 otherwise.
    -------------------------------------------------------------------------- */
 
-static inline void evacuate_large(StgPtr p)
+static inline void
+evacuate_large(StgPtr p, rtsBool mutable)
 {
   bdescr *bd = Bdescr(p);
+  step *step;
 
   /* should point to the beginning of the block */
   ASSERT(((W_)p & BLOCK_MASK) == 0);
   
   /* already evacuated? */
-  if (bd->step == 1) {
+  if (bd->evacuated) { 
+    /* Don't forget to set the failed_to_evac flag if we didn't get
+     * the desired destination (see comments in evacuate()).
+     */
+    if (bd->gen->no < evac_gen) {
+      failed_to_evac = rtsTrue;
+    }
     return;
   }
 
-  /* remove from large_alloc_list */
+  step = bd->step;
+  /* remove from large_object list */
   if (bd->back) {
     bd->back->link = bd->link;
   } else { /* first object in the list */
-    large_alloc_list = bd->link;
+    step->large_objects = bd->link;
   }
   if (bd->link) {
     bd->link->back = bd->back;
   }
   
-  /* link it on to the evacuated large object list */
-  bd->link = new_large_objects;
-  new_large_objects = bd;
-  bd->step = 1;
-}  
+  /* link it on to the evacuated large object list of the destination step
+   */
+  step = bd->step->to;
+  if (step->gen->no < evac_gen) {
+    step = &generations[evac_gen].steps[0];
+  }
+
+  bd->step = step;
+  bd->gen = step->gen;
+  bd->link = step->new_large_objects;
+  step->new_large_objects = bd;
+  bd->evacuated = 1;
+
+  if (mutable) {
+    evacuate_mutable((StgMutClosure *)p);
+  }
+}
+
+/* -----------------------------------------------------------------------------
+   Adding a MUT_CONS to an older generation.
+
+   This is necessary from time to time when we end up with an
+   old-to-new generation pointer in a non-mutable object.  We defer
+   the promotion until the next GC.
+   -------------------------------------------------------------------------- */
+
+static StgClosure *
+mkMutCons(StgClosure *ptr, generation *gen)
+{
+  StgMutVar *q;
+  step *step;
+
+  step = &gen->steps[0];
+
+  /* chain a new block onto the to-space for the destination step if
+   * necessary.
+   */
+  if (step->hp + sizeofW(StgIndOldGen) >= step->hpLim) {
+    addBlock(step);
+  }
+
+  q = (StgMutVar *)step->hp;
+  step->hp += sizeofW(StgMutVar);
+
+  SET_HDR(q,&MUT_CONS_info,CCS_GC);
+  q->var = ptr;
+  evacuate_mutable((StgMutClosure *)q);
+
+  return (StgClosure *)q;
+}
 
 /* -----------------------------------------------------------------------------
    Evacuate
 
    This is called (eventually) for every live object in the system.
+
+   The caller to evacuate specifies a desired generation in the
+   evac_gen global variable.  The following conditions apply to
+   evacuating an object which resides in generation M when we're
+   collecting up to generation N
+
+   if  M >= evac_gen 
+           if  M > N     do nothing
+          else          evac to step->to
+
+   if  M < evac_gen      evac to evac_gen, step 0
+
+   if the object is already evacuated, then we check which generation
+   it now resides in.
+
+   if  M >= evac_gen     do nothing
+   if  M <  evac_gen     set failed_to_evac flag to indicate that we
+                         didn't manage to evacuate this object into evac_gen.
+
    -------------------------------------------------------------------------- */
 
-static StgClosure *evacuate(StgClosure *q)
+
+static StgClosure *
+evacuate(StgClosure *q)
 {
   StgClosure *to;
+  bdescr *bd = NULL;
   const StgInfoTable *info;
 
 loop:
+  if (!LOOKS_LIKE_STATIC(q)) {
+    bd = Bdescr((P_)q);
+    if (bd->gen->no > N) {
+      /* Can't evacuate this object, because it's in a generation
+       * older than the ones we're collecting.  Let's hope that it's
+       * in evac_gen or older, or we will have to make an IND_OLDGEN object.
+       */
+      if (bd->gen->no < evac_gen) {
+       /* nope */
+       failed_to_evac = rtsTrue;
+      }
+      return q;
+    }
+  }
+
   /* make sure the info pointer is into text space */
   ASSERT(q && (LOOKS_LIKE_GHC_INFO(GET_INFO(q))
               || IS_HUGS_CONSTR_INFO(GET_INFO(q))));
@@ -556,10 +896,17 @@ loop:
   switch (info -> type) {
 
   case BCO:
-    to = copy(q,bco_sizeW(stgCast(StgBCO*,q)));
+    to = copy(q,bco_sizeW(stgCast(StgBCO*,q)),bd);
     upd_evacuee(q,to);
     return to;
 
+  case MUT_VAR:
+  case MVAR:
+    to = copy(q,sizeW_fromITBL(info),bd);
+    upd_evacuee(q,to);
+    evacuate_mutable((StgMutClosure *)to);
+    return to;
+
   case FUN:
   case THUNK:
   case CONSTR:
@@ -569,22 +916,20 @@ loop:
   case CAF_ENTERED:
   case WEAK:
   case FOREIGN:
-  case MUT_VAR:
-  case MVAR:
-    to = copy(q,sizeW_fromITBL(info));
+    to = copy(q,sizeW_fromITBL(info),bd);
     upd_evacuee(q,to);
     return to;
 
   case CAF_BLACKHOLE:
   case BLACKHOLE:
-    to = copy(q,BLACKHOLE_sizeW());
+    to = copy(q,BLACKHOLE_sizeW(),bd);
     upd_evacuee(q,to);
     return to;
 
   case THUNK_SELECTOR:
     {
       const StgInfoTable* selectee_info;
-      StgClosure* selectee = stgCast(StgSelector*,q)->selectee;
+      StgClosure* selectee = ((StgSelector*)q)->selectee;
 
     selector_loop:
       selectee_info = get_itbl(selectee);
@@ -606,7 +951,7 @@ loop:
           * with the evacuation, just update the source address with
           * a pointer to the (evacuated) constructor field.
           */
-         if (IS_USER_PTR(q) && Bdescr((P_)q)->step == 1) {
+         if (IS_USER_PTR(q) && Bdescr((P_)q)->evacuated) {
            return q;
          }
 
@@ -646,19 +991,28 @@ loop:
        barf("evacuate: THUNK_SELECTOR: strange selectee");
       }
     }
-    to = copy(q,THUNK_SELECTOR_sizeW());
+    to = copy(q,THUNK_SELECTOR_sizeW(),bd);
     upd_evacuee(q,to);
     return to;
 
   case IND:
   case IND_OLDGEN:
     /* follow chains of indirections, don't evacuate them */
-    q = stgCast(StgInd*,q)->indirectee;
+    q = ((StgInd*)q)->indirectee;
     goto loop;
 
-  case CONSTR_STATIC:
+    /* ToDo: optimise STATIC_LINK for known cases.
+       - FUN_STATIC       : payload[0]
+       - THUNK_STATIC     : payload[1]
+       - IND_STATIC       : payload[1]
+    */
   case THUNK_STATIC:
   case FUN_STATIC:
+    if (info->srt_len == 0) {  /* small optimisation */
+      return q;
+    }
+    /* fall through */
+  case CONSTR_STATIC:
   case IND_STATIC:
     /* don't want to evacuate these, but we do want to follow pointers
      * from SRTs  - see scavenge_static.
@@ -666,7 +1020,7 @@ loop:
 
     /* put the object on the static list, if necessary.
      */
-    if (STATIC_LINK(info,(StgClosure *)q) == NULL) {
+    if (major_gc && STATIC_LINK(info,(StgClosure *)q) == NULL) {
       STATIC_LINK(info,(StgClosure *)q) = static_objects;
       static_objects = (StgClosure *)q;
     }
@@ -697,33 +1051,62 @@ 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)));
+    to = copy(q,pap_sizeW(stgCast(StgPAP*,q)),bd);
     upd_evacuee(q,to);
     return to;
 
   case EVACUATED:
-    /* Already evacuated, just return the forwarding address */
-    return stgCast(StgEvacuated*,q)->evacuee;
+    /* Already evacuated, just return the forwarding address.
+     * HOWEVER: if the requested destination generation (evac_gen) is
+     * older than the actual generation (because the object was
+     * already evacuated to a younger generation) then we have to
+     * set the failed_to_evac flag to indicate that we couldn't 
+     * manage to promote the object to the desired generation.
+     */
+    if (evac_gen > 0) {                /* optimisation */
+      StgClosure *p = ((StgEvacuated*)q)->evacuee;
+      if (Bdescr((P_)p)->gen->no < evac_gen) {
+       /*      fprintf(stderr,"evac failed!\n");*/
+       failed_to_evac = rtsTrue;
+      } 
+    }
+    return ((StgEvacuated*)q)->evacuee;
 
   case MUT_ARR_WORDS:
   case ARR_WORDS:
-  case MUT_ARR_PTRS:
-  case MUT_ARR_PTRS_FROZEN:
-  case ARR_PTRS:
     {
       nat size = arr_words_sizeW(stgCast(StgArrWords*,q)); 
 
       if (size >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
-       evacuate_large((P_)q);
+       evacuate_large((P_)q, rtsFalse);
        return q;
       } else {
        /* just copy the block */
-       to = copy(q,size);
+       to = copy(q,size,bd);
        upd_evacuee(q,to);
        return to;
       }
     }
 
+  case MUT_ARR_PTRS:
+  case MUT_ARR_PTRS_FROZEN:
+    {
+      nat size = mut_arr_ptrs_sizeW(stgCast(StgMutArrPtrs*,q)); 
+
+      if (size >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
+       evacuate_large((P_)q, info->type == MUT_ARR_PTRS);
+       to = q;
+      } else {
+       /* just copy the block */
+       to = copy(q,size,bd);
+       upd_evacuee(q,to);
+       if (info->type == MUT_ARR_PTRS) {
+         evacuate_mutable((StgMutClosure *)to);
+       }
+      }
+      return to;
+    }
+
   case TSO:
     {
       StgTSO *tso = stgCast(StgTSO *,q);
@@ -733,14 +1116,15 @@ loop:
       /* Large TSOs don't get moved, so no relocation is required.
        */
       if (size >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
-       evacuate_large((P_)q);
+       evacuate_large((P_)q, rtsFalse);
+       tso->mut_link = NULL;   /* see below */
        return q;
 
       /* To evacuate a small TSO, we need to relocate the update frame
        * list it contains.  
        */
       } else {
-       StgTSO *new_tso = (StgTSO *)copy((StgClosure *)tso,tso_sizeW(tso));
+       StgTSO *new_tso = (StgTSO *)copy((StgClosure *)tso,tso_sizeW(tso),bd);
 
        diff = (StgPtr)new_tso - (StgPtr)tso; /* In *words* */
 
@@ -751,6 +1135,15 @@ loop:
        
        relocate_TSO(tso, new_tso);
        upd_evacuee(q,(StgClosure *)new_tso);
+
+       /* don't evac_mutable - these things are marked mutable as
+        * required.  We *do* need to zero the mut_link field, though:
+        * this TSO might have been on the mutable list for this
+        * generation, but we're collecting this generation anyway so
+        * we didn't follow the mutable list.
+        */
+       new_tso->mut_link = NULL;
+
        return (StgClosure *)new_tso;
       }
     }
@@ -820,7 +1213,7 @@ relocate_TSO(StgTSO *src, StgTSO *dest)
 }
 
 static inline void
-evacuate_srt(const StgInfoTable *info)
+scavenge_srt(const StgInfoTable *info)
 {
   StgClosure **srt, **srt_end;
 
@@ -835,29 +1228,48 @@ evacuate_srt(const StgInfoTable *info)
   }
 }
 
-static StgPtr
-scavenge(StgPtr to_scan)
+/* -----------------------------------------------------------------------------
+   Scavenge a given step until there are no more objects in this step
+   to scavenge.
+
+   evac_gen is set by the caller to be either zero (for a step in a
+   generation < N) or G where G is the generation of the step being
+   scavenged.  
+
+   We sometimes temporarily change evac_gen back to zero if we're
+   scavenging a mutable object where early promotion isn't such a good
+   idea.  
+   -------------------------------------------------------------------------- */
+   
+
+static void
+scavenge(step *step)
 {
-  StgPtr p;
+  StgPtr p, q;
   const StgInfoTable *info;
   bdescr *bd;
+  nat saved_evac_gen = evac_gen; /* used for temporarily changing evac_gen */
+
+  p = step->scan;
+  bd = step->scan_bd;
 
-  p = to_scan;
-  bd = Bdescr((P_)p);
+  failed_to_evac = rtsFalse;
 
   /* scavenge phase - standard breadth-first scavenging of the
    * evacuated objects 
    */
 
-  while (bd != toHp_bd || p < toHp) {
+  while (bd != step->hp_bd || p < step->hp) {
 
     /* If we're at the end of this block, move on to the next block */
-    if (bd != toHp_bd && p == bd->free) {
+    if (bd != step->hp_bd && p == bd->free) {
       bd = bd->link;
       p = bd->start;
       continue;
     }
 
+    q = p;                     /* save ptr to object */
+
     ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO((StgClosure *)p))
                 || IS_HUGS_CONSTR_INFO(GET_INFO((StgClosure *)p))));
 
@@ -872,19 +1284,32 @@ scavenge(StgPtr to_scan)
          bcoConstCPtr(bco,i) = evacuate(bcoConstCPtr(bco,i));
        }
        p += bco_sizeW(bco);
-       continue;
+       break;
+      }
+
+    case MVAR:
+      /* treat MVars specially, because we don't want to evacuate the
+       * mut_link field in the middle of the closure.
+       */
+      { 
+       StgMVar *mvar = ((StgMVar *)p);
+       evac_gen = 0;
+       (StgClosure *)mvar->head = evacuate((StgClosure *)mvar->head);
+       (StgClosure *)mvar->tail = evacuate((StgClosure *)mvar->tail);
+       (StgClosure *)mvar->value = evacuate((StgClosure *)mvar->value);
+       p += sizeofW(StgMVar);
+       evac_gen = saved_evac_gen;
+       break;
       }
 
     case FUN:
     case THUNK:
-      evacuate_srt(info);
+      scavenge_srt(info);
       /* fall through */
 
     case CONSTR:
     case WEAK:
     case FOREIGN:
-    case MVAR:
-    case MUT_VAR:
     case IND_PERM:
     case IND_OLDGEN_PERM:
     case CAF_UNENTERED:
@@ -897,9 +1322,19 @@ scavenge(StgPtr to_scan)
          (StgClosure *)*p = evacuate((StgClosure *)*p);
        }
        p += info->layout.payload.nptrs;
-       continue;
+       break;
       }
 
+    case MUT_VAR:
+      /* ignore MUT_CONSs */
+      if (((StgMutVar *)p)->header.info != &MUT_CONS_info) {
+       evac_gen = 0;
+       ((StgMutVar *)p)->var = evacuate(((StgMutVar *)p)->var);
+       evac_gen = saved_evac_gen;
+      }
+      p += sizeofW(StgMutVar);
+      break;
+
     case CAF_BLACKHOLE:
     case BLACKHOLE:
       { 
@@ -907,7 +1342,7 @@ scavenge(StgPtr to_scan)
        (StgClosure *)bh->blocking_queue = 
          evacuate((StgClosure *)bh->blocking_queue);
        p += BLACKHOLE_sizeW();
-       continue;
+       break;
       }
 
     case THUNK_SELECTOR:
@@ -915,7 +1350,7 @@ scavenge(StgPtr to_scan)
        StgSelector *s = (StgSelector *)p;
        s->selectee = evacuate(s->selectee);
        p += THUNK_SELECTOR_sizeW();
-       continue;
+       break;
       }
 
     case IND:
@@ -956,27 +1391,44 @@ scavenge(StgPtr to_scan)
        pap->fun = evacuate(pap->fun);
        scavenge_stack((P_)pap->payload, (P_)pap->payload + pap->n_args);
        p += pap_sizeW(pap);
-       continue;
+       break;
       }
       
     case ARR_WORDS:
     case MUT_ARR_WORDS:
       /* nothing to follow */
       p += arr_words_sizeW(stgCast(StgArrWords*,p));
-      continue;
+      break;
 
-    case ARR_PTRS:
     case MUT_ARR_PTRS:
-    case MUT_ARR_PTRS_FROZEN:
       /* follow everything */
       {
        StgPtr next;
 
-       next = p + arr_ptrs_sizeW(stgCast(StgArrPtrs*,p));
-       for (p = (P_)((StgArrPtrs *)p)->payload; p < next; p++) {
+       evac_gen = 0;           /* repeatedly mutable */
+       next = p + mut_arr_ptrs_sizeW((StgMutArrPtrs*)p);
+       for (p = (P_)((StgMutArrPtrs *)p)->payload; p < next; p++) {
          (StgClosure *)*p = evacuate((StgClosure *)*p);
        }
-       continue;
+       evac_gen = saved_evac_gen;
+       break;
+      }
+
+    case MUT_ARR_PTRS_FROZEN:
+      /* follow everything */
+      {
+       StgPtr start = p, next;
+
+       next = p + mut_arr_ptrs_sizeW((StgMutArrPtrs*)p);
+       for (p = (P_)((StgMutArrPtrs *)p)->payload; p < next; p++) {
+         (StgClosure *)*p = evacuate((StgClosure *)*p);
+       }
+       if (failed_to_evac) {
+         /* we can do this easier... */
+         evacuate_mutable((StgMutClosure *)start);
+         failed_to_evac = rtsFalse;
+       }
+       break;
       }
 
     case TSO:
@@ -984,12 +1436,14 @@ scavenge(StgPtr to_scan)
        StgTSO *tso;
        
        tso = (StgTSO *)p;
+       evac_gen = 0;
        /* chase the link field for any TSOs on the same queue */
        (StgClosure *)tso->link = evacuate((StgClosure *)tso->link);
        /* scavenge this thread's stack */
        scavenge_stack(tso->sp, &(tso->stack[tso->stack_size]));
+       evac_gen = saved_evac_gen;
        p += tso_sizeW(tso);
-       continue;
+       break;
       }
 
     case BLOCKED_FETCH:
@@ -1000,12 +1454,253 @@ scavenge(StgPtr to_scan)
     default:
       barf("scavenge");
     }
+
+    /* If we didn't manage to promote all the objects pointed to by
+     * the current object, then we have to designate this object as
+     * mutable (because it contains old-to-new generation pointers).
+     */
+    if (failed_to_evac) {
+      mkMutCons((StgClosure *)q, &generations[evac_gen]);
+      failed_to_evac = rtsFalse;
+    }
   }
-  return (P_)p;
+
+  step->scan_bd = bd;
+  step->scan = p;
 }    
 
-/* scavenge_static is the scavenge code for a static closure.
- */
+/* -----------------------------------------------------------------------------
+   Scavenge one object.
+
+   This is used for objects that are temporarily marked as mutable
+   because they contain old-to-new generation pointers.  Only certain
+   objects can have this property.
+   -------------------------------------------------------------------------- */
+static rtsBool
+scavenge_one(StgPtr p)
+{
+  StgInfoTable *info;
+  rtsBool no_luck;
+
+  ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO((StgClosure *)p))
+              || IS_HUGS_CONSTR_INFO(GET_INFO((StgClosure *)p))));
+
+  info = get_itbl((StgClosure *)p);
+
+  switch (info -> type) {
+
+  case FUN:
+  case THUNK:
+  case CONSTR:
+  case WEAK:
+  case FOREIGN:
+  case IND_PERM:
+  case IND_OLDGEN_PERM:
+  case CAF_UNENTERED:
+  case CAF_ENTERED:
+    {
+      StgPtr end;
+      
+      end = (P_)((StgClosure *)p)->payload + info->layout.payload.ptrs;
+      for (p = (P_)((StgClosure *)p)->payload; p < end; p++) {
+       (StgClosure *)*p = evacuate((StgClosure *)*p);
+      }
+      break;
+    }
+
+  case CAF_BLACKHOLE:
+  case BLACKHOLE:
+    { 
+      StgBlackHole *bh = (StgBlackHole *)p;
+      (StgClosure *)bh->blocking_queue = 
+       evacuate((StgClosure *)bh->blocking_queue);
+      break;
+    }
+
+  case THUNK_SELECTOR:
+    { 
+      StgSelector *s = (StgSelector *)p;
+      s->selectee = evacuate(s->selectee);
+       break;
+    }
+    
+  case AP_UPD: /* same as PAPs */
+  case PAP:
+    /* Treat a PAP just like a section of stack, not forgetting to
+     * evacuate the function pointer too...
+     */
+    { 
+      StgPAP* pap = stgCast(StgPAP*,p);
+      
+      pap->fun = evacuate(pap->fun);
+      scavenge_stack((P_)pap->payload, (P_)pap->payload + pap->n_args);
+      break;
+    }
+
+  case IND_OLDGEN:
+    /* This might happen if for instance a MUT_CONS was pointing to a
+     * THUNK which has since been updated.  The IND_OLDGEN will
+     * be on the mutable list anyway, so we don't need to do anything
+     * here.
+     */
+    break;
+
+  default:
+    barf("scavenge_one: strange object");
+  }    
+
+  no_luck = failed_to_evac;
+  failed_to_evac = rtsFalse;
+  return (no_luck);
+}
+
+
+/* -----------------------------------------------------------------------------
+   Scavenging mutable lists.
+
+   We treat the mutable list of each generation > N (i.e. all the
+   generations older than the one being collected) as roots.  We also
+   remove non-mutable objects from the mutable list at this point.
+   -------------------------------------------------------------------------- */
+
+static StgMutClosure *
+scavenge_mutable_list(StgMutClosure *p, nat gen)
+{
+  StgInfoTable *info;
+  StgMutClosure *start;
+  StgMutClosure **prev;
+
+  evac_gen = 0;
+
+  prev = &start;
+  start = p;
+
+  failed_to_evac = rtsFalse;
+
+  for (; p != END_MUT_LIST; p = *prev) {
+
+    /* make sure the info pointer is into text space */
+    ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO(p))
+                || IS_HUGS_CONSTR_INFO(GET_INFO(p))));
+    
+    info = get_itbl(p);
+    switch(info->type) {
+      
+    case MUT_ARR_PTRS_FROZEN:
+      /* remove this guy from the mutable list, but follow the ptrs
+       * anyway (and make sure they get promoted to this gen).
+       */
+      {
+       StgPtr end, q;
+       
+       end = (P_)p + mut_arr_ptrs_sizeW((StgMutArrPtrs*)p);
+       evac_gen = gen;
+       for (q = (P_)((StgMutArrPtrs *)p)->payload; q < end; q++) {
+         (StgClosure *)*q = evacuate((StgClosure *)*q);
+       }
+       evac_gen = 0;
+
+       if (failed_to_evac) {
+         failed_to_evac = rtsFalse;
+         prev = &p->mut_link;
+       } else {
+         *prev = p->mut_link;
+       }
+       continue;
+      }
+
+    case MUT_ARR_PTRS:
+      /* follow everything */
+      prev = &p->mut_link;
+      {
+       StgPtr end, q;
+       
+       end = (P_)p + mut_arr_ptrs_sizeW((StgMutArrPtrs*)p);
+       for (q = (P_)((StgMutArrPtrs *)p)->payload; q < end; q++) {
+         (StgClosure *)*q = evacuate((StgClosure *)*q);
+       }
+       continue;
+      }
+      
+    case MUT_VAR:
+      /* MUT_CONS is a kind of MUT_VAR, except that we try to remove
+       * it from the mutable list if possible by promoting whatever it
+       * points to.
+       */
+      if (p->header.info == &MUT_CONS_info) {
+       evac_gen = gen;
+       if (scavenge_one((P_)((StgMutVar *)p)->var) == rtsTrue) {
+         /* didn't manage to promote everything, so leave the
+          * MUT_CONS on the list.
+          */
+         prev = &p->mut_link;
+       } else {
+         *prev = p->mut_link;
+       }
+       evac_gen = 0;
+      } else {
+       ((StgMutVar *)p)->var = evacuate(((StgMutVar *)p)->var);
+       prev = &p->mut_link;
+      }
+      continue;
+      
+    case TSO:
+      /* follow ptrs and remove this from the mutable list */
+      { 
+       StgTSO *tso = (StgTSO *)p;
+
+       /* Don't bother scavenging if this thread is dead 
+        */
+       if (!(tso->whatNext == ThreadComplete ||
+             tso->whatNext == ThreadKilled)) {
+         /* Don't need to chase the link field for any TSOs on the
+          * same queue. Just scavenge this thread's stack 
+          */
+         scavenge_stack(tso->sp, &(tso->stack[tso->stack_size]));
+       }
+
+       /* Don't take this TSO off the mutable list - it might still
+        * point to some younger objects (because we set evac_gen to 0
+        * above). 
+        */
+       prev = &tso->mut_link;
+       continue;
+      }
+      
+    case IND_OLDGEN:
+    case IND_OLDGEN_PERM:
+    case IND_STATIC:
+      /* Try to pull the indirectee into this generation, so we can
+       * remove the indirection from the mutable list.  
+       */
+      evac_gen = gen;
+      ((StgIndOldGen *)p)->indirectee = 
+        evacuate(((StgIndOldGen *)p)->indirectee);
+      evac_gen = 0;
+
+      if (failed_to_evac) {
+       failed_to_evac = rtsFalse;
+       prev = &p->mut_link;
+      } else {
+       *prev = p->mut_link;
+       /* the mut_link field of an IND_STATIC is overloaded as the
+        * static link field too (it just so happens that we don't need
+        * both at the same time), so we need to NULL it out when
+        * removing this object from the mutable list because the static
+        * link fields are all assumed to be NULL before doing a major
+        * collection. 
+        */
+       p->mut_link = NULL;
+      }
+      continue;
+      
+    default:
+      /* shouldn't have anything else on the mutables list */
+      barf("scavenge_mutable_object: non-mutable object?");
+    }
+  }
+  return start;
+}
 
 static void
 scavenge_static(void)
@@ -1013,26 +1708,29 @@ scavenge_static(void)
   StgClosure* p = static_objects;
   const StgInfoTable *info;
 
+  /* Always evacuate straight to the oldest generation for static
+   * objects */
+  evac_gen = oldest_gen->no;
+
   /* keep going until we've scavenged all the objects on the linked
      list... */
   while (p != END_OF_STATIC_LIST) {
 
+    info = get_itbl(p);
+
     /* make sure the info pointer is into text space */
-    ASSERT(p && LOOKS_LIKE_GHC_INFO(GET_INFO(p)));
     ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO(p))
                 || IS_HUGS_CONSTR_INFO(GET_INFO(p))));
-
-    info = get_itbl(p);
-
+    
     /* Take this object *off* the static_objects list,
      * and put it on the scavenged_static_objects list.
      */
     static_objects = STATIC_LINK(info,p);
     STATIC_LINK(info,p) = scavenged_static_objects;
     scavenged_static_objects = p;
-
+    
     switch (info -> type) {
-
+      
     case IND_STATIC:
       {
        StgInd *ind = (StgInd *)p;
@@ -1042,9 +1740,9 @@ scavenge_static(void)
       
     case THUNK_STATIC:
     case FUN_STATIC:
-      evacuate_srt(info);
+      scavenge_srt(info);
       /* fall through */
-
+      
     case CONSTR_STATIC:
       {        
        StgPtr q, next;
@@ -1145,21 +1843,22 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
        StgClosure *to;
        StgClosureType type = get_itbl(frame->updatee)->type;
 
+       p += sizeofW(StgUpdateFrame);
        if (type == EVACUATED) {
          frame->updatee = evacuate(frame->updatee);
-         p += sizeofW(StgUpdateFrame);
          continue;
        } else {
+         bdescr *bd = Bdescr((P_)frame->updatee);
          ASSERT(type == BLACKHOLE || type == CAF_BLACKHOLE);
-         to = copy(frame->updatee, BLACKHOLE_sizeW());
+         if (bd->gen->no >= evac_gen && bd->gen->no > N) { continue; }
+         to = copy(frame->updatee, BLACKHOLE_sizeW(), bd);
          upd_evacuee(frame->updatee,to);
          frame->updatee = to;
-         p += sizeofW(StgUpdateFrame);
          continue;
        }
       }
 
-      /* small bitmap (< 32 entries) */
+      /* small bitmap (< 32 entries, or 64 on a 64-bit machine) */
     case RET_BCO:
     case RET_SMALL:
     case RET_VEC_SMALL:
@@ -1178,7 +1877,7 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
       }
       
     follow_srt:
-      evacuate_srt(info);
+      scavenge_srt(info);
       continue;
 
       /* large bitmap (> 32 entries) */
@@ -1222,32 +1921,33 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
 
 /*-----------------------------------------------------------------------------
   scavenge the large object list.
+
+  evac_gen set by caller; similar games played with evac_gen as with
+  scavenge() - see comment at the top of scavenge().  Most large
+  objects are (repeatedly) mutable, so most of the time evac_gen will
+  be zero.
   --------------------------------------------------------------------------- */
 
 static void
-scavenge_large(void)
+scavenge_large(step *step)
 {
   bdescr *bd;
   StgPtr p;
   const StgInfoTable* info;
+  nat saved_evac_gen = evac_gen; /* used for temporarily changing evac_gen */
 
-  bd = new_large_objects;
+  evac_gen = 0;                        /* most objects are mutable */
+  bd = step->new_large_objects;
 
-  for (; bd != NULL; bd = new_large_objects) {
+  for (; bd != NULL; bd = step->new_large_objects) {
 
     /* take this object *off* the large objects list and put it on
      * the scavenged large objects list.  This is so that we can
      * treat new_large_objects as a stack and push new objects on
      * the front when evacuating.
      */
-    new_large_objects = bd->link;
-    /* scavenged_large_objects is doubly linked */
-    bd->link = scavenged_large_objects;
-    bd->back = NULL;
-    if (scavenged_large_objects) {
-      scavenged_large_objects->back = bd;
-    }
-    scavenged_large_objects = bd;
+    step->new_large_objects = bd->link;
+    dbl_link_onto(bd, &step->scavenged_large_objects);
 
     p = bd->start;
     info  = get_itbl(stgCast(StgClosure*,p));
@@ -1261,27 +1961,44 @@ scavenge_large(void)
       /* nothing to follow */
       continue;
 
-    case ARR_PTRS:
     case MUT_ARR_PTRS:
-    case MUT_ARR_PTRS_FROZEN:
       /* follow everything */
       {
        StgPtr next;
 
-       next = p + arr_ptrs_sizeW(stgCast(StgArrPtrs*,p));
-       for (p = (P_)((StgArrPtrs *)p)->payload; p < next; p++) {
+       next = p + mut_arr_ptrs_sizeW((StgMutArrPtrs*)p);
+       for (p = (P_)((StgMutArrPtrs *)p)->payload; p < next; p++) {
          (StgClosure *)*p = evacuate((StgClosure *)*p);
        }
        continue;
       }
 
+    case MUT_ARR_PTRS_FROZEN:
+      /* follow everything */
+      {
+       StgPtr start = p, next;
+
+       evac_gen = saved_evac_gen; /* not really mutable */
+       next = p + mut_arr_ptrs_sizeW((StgMutArrPtrs*)p);
+       for (p = (P_)((StgMutArrPtrs *)p)->payload; p < next; p++) {
+         (StgClosure *)*p = evacuate((StgClosure *)*p);
+       }
+       evac_gen = 0;
+       if (failed_to_evac) {
+         evacuate_mutable((StgMutClosure *)start);
+       }
+       continue;
+      }
+
     case BCO:
       {
        StgBCO* bco = stgCast(StgBCO*,p);
        nat i;
+       evac_gen = saved_evac_gen;
        for (i = 0; i < bco->n_ptrs; i++) {
          bcoConstCPtr(bco,i) = evacuate(bcoConstCPtr(bco,i));
        }
+       evac_gen = 0;
        continue;
       }
 
@@ -1302,6 +2019,7 @@ scavenge_large(void)
     }
   }
 }
+
 static void
 zeroStaticObjectList(StgClosure* first_static)
 {
@@ -1316,23 +2034,40 @@ zeroStaticObjectList(StgClosure* first_static)
   }
 }
 
+/* This function is only needed because we share the mutable link
+ * field with the static link field in an IND_STATIC, so we have to
+ * zero the mut_link field before doing a major GC, which needs the
+ * static link field.  
+ *
+ * It doesn't do any harm to zero all the mutable link fields on the
+ * mutable list.
+ */
+static void
+zeroMutableList(StgMutClosure *first)
+{
+  StgMutClosure *next, *c;
+
+  for (c = first; c != END_MUT_LIST; c = next) {
+    next = c->mut_link;
+    c->mut_link = NULL;
+  }
+}
+
 /* -----------------------------------------------------------------------------
    Reverting CAFs
-
    -------------------------------------------------------------------------- */
 
 void RevertCAFs(void)
 {
-    while (enteredCAFs != END_CAF_LIST) {
-       StgCAF* caf = enteredCAFs;
-       const StgInfoTable *info = get_itbl(caf);
-
-       enteredCAFs = caf->link;
-       ASSERT(get_itbl(caf)->type == CAF_ENTERED);
-       SET_INFO(caf,&CAF_UNENTERED_info);
-       caf->value = stgCast(StgClosure*,0xdeadbeef);
-       caf->link  = stgCast(StgCAF*,0xdeadbeef);
-    }
+  while (enteredCAFs != END_CAF_LIST) {
+    StgCAF* caf = enteredCAFs;
+    
+    enteredCAFs = caf->link;
+    ASSERT(get_itbl(caf)->type == CAF_ENTERED);
+    SET_INFO(caf,&CAF_UNENTERED_info);
+    caf->value = stgCast(StgClosure*,0xdeadbeef);
+    caf->link  = stgCast(StgCAF*,0xdeadbeef);
+  }
 }
 
 void revertDeadCAFs(void)
@@ -1455,7 +2190,7 @@ threadLazyBlackHole(StgTSO *tso)
       if (bh->header.info != &BLACKHOLE_info
          && bh->header.info != &CAF_BLACKHOLE_info) {
        SET_INFO(bh,&BLACKHOLE_info);
-       bh->blocking_queue = stgCast(StgTSO*,&END_TSO_QUEUE_closure);
+       bh->blocking_queue = END_TSO_QUEUE;
       }
 
       update_frame = update_frame->link;
@@ -1619,7 +2354,7 @@ threadSqueezeStack(StgTSO *tso)
            && bh->header.info != &CAF_BLACKHOLE_info
            ) {
          SET_INFO(bh,&BLACKHOLE_info);
-         bh->blocking_queue = stgCast(StgTSO*,&END_TSO_QUEUE_closure);
+         bh->blocking_queue = END_TSO_QUEUE;
        }
       }
 
index 61bbbf7..3c5225f 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: MBlock.c,v 1.2 1998/12/02 13:28:28 simonm Exp $
+ * $Id: MBlock.c,v 1.3 1999/01/13 17:25:40 simonm Exp $
  *
  * MegaBlock Allocator Interface.  This file contains all the dirty
  * architecture-dependent hackery required to get a chunk of aligned
@@ -62,6 +62,8 @@
 /* ToDo: memory locations on other architectures */
 #endif
 
+lnat mblocks_allocated = 0;
+
 void *
 getMBlock(void)
 {
@@ -134,5 +136,7 @@ getMBlocks(nat n)
 
   next_request += size;
 
+  mblocks_allocated += n;
+  
   return ret;
 }
index 094c4fe..0fb902e 100644 (file)
@@ -1,9 +1,11 @@
 /* -----------------------------------------------------------------------------
- * $Id: MBlock.h,v 1.2 1998/12/02 13:28:30 simonm Exp $
+ * $Id: MBlock.h,v 1.3 1999/01/13 17:25:41 simonm Exp $
  *
  * MegaBlock Allocator interface.
  *
  * ---------------------------------------------------------------------------*/
 
+extern lnat mblocks_allocated;
+
 extern void * getMBlock(void);
 extern void * getMBlocks(nat n);
index 83bc744..a1b7711 100644 (file)
@@ -1,5 +1,5 @@
 #-----------------------------------------------------------------------------
-# $Id: Makefile,v 1.2 1998/12/02 13:28:32 simonm Exp $
+# $Id: Makefile,v 1.3 1999/01/13 17:25:41 simonm Exp $
 
 #  This is the Makefile for the runtime-system stuff.
 #  This stuff is written in C (and cannot be written in Haskell).
index 0d16ae6..9c7eb6f 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: PrimOps.hc,v 1.2 1998/12/02 13:28:32 simonm Exp $
+ * $Id: PrimOps.hc,v 1.3 1999/01/13 17:25:41 simonm Exp $
  *
  * Primitive functions / data
  *
@@ -213,21 +213,21 @@ newByteArray(StablePtr, sizeof(StgStablePtr));
 FN_(newArrayZh_fast)
 {
   W_ size, n, init;
-  StgArrPtrs* arr;
+  StgMutArrPtrs* arr;
   StgPtr p;
   FB_
     n = R1.w;
 
     MAYBE_GC(R2_PTR,newArrayZh_fast);
 
-    size = sizeofW(StgArrPtrs) + n;
-    arr = (StgArrPtrs *)allocate(size);
+    size = sizeofW(StgMutArrPtrs) + n;
+    arr = (StgMutArrPtrs *)allocate(size);
 
     SET_HDR(arr,&MUT_ARR_PTRS_info,CCCS);
     arr->ptrs = n;
 
     init = R2.w;
-    for (p = (P_)arr + sizeofW(StgArrPtrs); 
+    for (p = (P_)arr + sizeofW(StgMutArrPtrs); 
         p < (P_)arr + size; p++) {
        *p = (W_)init;
     }
index 530ff9f..19c522e 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: RtsFlags.c,v 1.2 1998/12/02 13:28:39 simonm Exp $
+ * $Id: RtsFlags.c,v 1.3 1999/01/13 17:25:42 simonm Exp $
  *
  * Functions for parsing the argument list.
  *
@@ -64,8 +64,8 @@ void initRtsFlagsDefaults(void)
     RtsFlags.GcFlags.minAllocAreaSize   = (256 * 1024)        / BLOCK_SIZE;
     RtsFlags.GcFlags.maxHeapSize       = (64  * 1024 * 1024) / BLOCK_SIZE;
     RtsFlags.GcFlags.pcFreeHeap                = 3;    /* 3% */
+    RtsFlags.GcFlags.generations        = 2;
 
-    RtsFlags.GcFlags.force2s           = rtsFalse;
     RtsFlags.GcFlags.forceGC           = rtsFalse;
     RtsFlags.GcFlags.forcingInterval   = 5000000; /* 5MB (or words?) */
     RtsFlags.GcFlags.ringBell          = rtsFalse;
@@ -213,6 +213,7 @@ usage_text[] = {
 "  -A<size> Sets the minimum allocation area size (default 256k) Egs: -A1m -A10k",
 "  -M<size> Sets the maximum heap size (default 64M)  Egs: -H256k -H1G",
 "  -m<n>%   Minimum % of heap which must be available (default 3%)",
+"  -G<n>    Number of generations (default: 2)",
 "  -s<file> Summary GC statistics   (default file: <program>.stat)",
 "  -S<file> Detailed GC statistics  (with -Sstderr going to stderr)",
 "",
@@ -435,14 +436,6 @@ error = rtsTrue;
                break;
 #endif
 
-             case 'F':
-               if (strequal(rts_argv[arg]+2, "2s")) {
-                   RtsFlags.GcFlags.force2s = rtsTrue;
-               } else {
-                   bad_option( rts_argv[arg] );
-               }
-               break;
-
              case 'K':
                RtsFlags.GcFlags.maxStkSize = 
                  decode(rts_argv[arg]+2) / sizeof(W_);
@@ -477,6 +470,13 @@ error = rtsTrue;
                  bad_option( rts_argv[arg] );
                break;
 
+             case 'G':
+               RtsFlags.GcFlags.generations = decode(rts_argv[arg]+2);
+               if (RtsFlags.GcFlags.generations <= 1) {
+                 bad_option(rts_argv[arg]);
+               }
+               break;
+
              case 'H':
                /* ignore for compatibility with older versions */
                break;
index 1939ebe..7d2982b 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: RtsFlags.h,v 1.2 1998/12/02 13:28:40 simonm Exp $
+ * $Id: RtsFlags.h,v 1.3 1999/01/13 17:25:43 simonm Exp $
  *
  * Datatypes that holds the command-line flag settings.
  *
@@ -23,9 +23,8 @@ struct GC_FLAGS {
     nat     minAllocAreaSize;   /* in *blocks* */
     double  pcFreeHeap;
 
-    rtsBool force2s; /* force the use of 2-space copying collection;
-                       forced to rtsTrue if we do *heap* profiling.
-                    */
+    nat     generations;
+
     rtsBool forceGC; /* force a major GC every <interval> bytes */
     int            forcingInterval; /* actually, stored as a number of *words* */
     rtsBool ringBell;
index 1977aab..874533a 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Sanity.c,v 1.2 1998/12/02 13:28:43 simonm Exp $
+ * $Id: Sanity.c,v 1.3 1999/01/13 17:25:43 simonm Exp $
  *
  * Sanity checking code for the heap and stack.
  *
 #include "BlockAlloc.h"
 #include "Sanity.h"
 
-static nat heap_step;
-
 #define LOOKS_LIKE_PTR(r) \
-  (IS_DATA_PTR(r) || ((IS_USER_PTR(r) && Bdescr((P_)r)->step == heap_step)))
+  (IS_DATA_PTR(r) || ((IS_USER_PTR(r) && Bdescr((P_)r)->free != (void *)-1)))
 
 /* -----------------------------------------------------------------------------
    Check stack sanity
@@ -199,6 +197,7 @@ checkClosure( StgClosure* p )
     case THUNK:
     case CONSTR:
     case IND_PERM:
+    case IND_OLDGEN:
     case IND_OLDGEN_PERM:
     case CAF_UNENTERED:
     case CAF_ENTERED:
@@ -241,14 +240,16 @@ checkClosure( StgClosure* p )
            return sizeofW(StgHeader) + MIN_UPD_SIZE;
 
     case IND:
-    case IND_OLDGEN:
        { 
            /* we don't expect to see any of these after GC
             * but they might appear during execution
             */
+           P_ q;
            StgInd *ind = stgCast(StgInd*,p);
            ASSERT(LOOKS_LIKE_PTR(ind->indirectee));
-           return sizeofW(StgInd);
+           q = (P_)p + sizeofW(StgInd);
+           while (!*q) { q++; }; /* skip padding words (see GC.c: evacuate())*/
+           return q - (P_)p;
        }
 
     case RET_BCO:
@@ -278,20 +279,19 @@ checkClosure( StgClosure* p )
     case MUT_ARR_WORDS:
            return arr_words_sizeW(stgCast(StgArrWords*,p));
 
-    case ARR_PTRS:
     case MUT_ARR_PTRS:
     case MUT_ARR_PTRS_FROZEN:
        {
-           StgArrPtrs* a = stgCast(StgArrPtrs*,p);
+           StgMutArrPtrs* a = stgCast(StgMutArrPtrs*,p);
            nat i;
            for (i = 0; i < a->ptrs; i++) {
-               ASSERT(LOOKS_LIKE_PTR(payloadPtr(a,i)));
+               ASSERT(LOOKS_LIKE_PTR(a->payload[i]));
            }
-           return arr_ptrs_sizeW(a);
+           return mut_arr_ptrs_sizeW(a);
        }
 
     case TSO:
-        checkTSO((StgTSO *)p, heap_step);
+        checkTSO((StgTSO *)p);
         return tso_sizeW((StgTSO *)p);
 
     case BLOCKED_FETCH:
@@ -309,27 +309,44 @@ checkClosure( StgClosure* p )
 
    After garbage collection, the live heap is in a state where we can
    run through and check that all the pointers point to the right
-   place.
+   place.  This function starts at a given position and sanity-checks
+   all the objects in the remainder of the chain.
    -------------------------------------------------------------------------- */
 
 extern void 
-checkHeap(bdescr *bd, nat step)
+checkHeap(bdescr *bd, StgPtr start)
 {
     StgPtr p;
 
-    heap_step = step;
+    if (start == NULL) {
+      p = bd->start;
+    } else {
+      p = start;
+    }
 
     while (bd != NULL) {
-      p = bd->start;
       while (p < bd->free) {
         nat size = checkClosure(stgCast(StgClosure*,p));
         /* This is the smallest size of closure that can live in the heap. */
         ASSERT( size >= MIN_NONUPD_SIZE + sizeofW(StgHeader) );
        p += size;
+       while (*p == 0) { p++; } /* skip over slop */
       }
       bd = bd->link;
+      if (bd != NULL) {
+       p = bd->start;
+      }
     }
-}    
+}
+
+extern void
+checkChain(bdescr *bd)
+{
+  while (bd != NULL) {
+    checkClosure((StgClosure *)bd->start);
+    bd = bd->link;
+  }
+}
 
 /* check stack - making sure that update frames are linked correctly */
 void 
@@ -361,7 +378,7 @@ checkStack(StgPtr sp, StgPtr stack_end, StgUpdateFrame* su )
 }
 
 extern void
-checkTSO(StgTSO *tso, nat step)
+checkTSO(StgTSO *tso)
 {
     StgPtr sp = tso->sp;
     StgPtr stack = tso->stack;
@@ -369,7 +386,12 @@ checkTSO(StgTSO *tso, nat step)
     StgOffset stack_size = tso->stack_size;
     StgPtr stack_end = stack + stack_size;
 
-    heap_step = step;
+    if (tso->whatNext == ThreadComplete ||  tso->whatNext == ThreadKilled) {
+      /* The garbage collector doesn't bother following any pointers
+       * from dead threads, so don't check sanity here.  
+       */
+      return;
+    }
 
     ASSERT(stack <= sp && sp < stack_end);
     ASSERT(sp <= stgCast(StgPtr,su));
index 7fc6b4f..581e029 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Sanity.h,v 1.2 1998/12/02 13:28:44 simonm Exp $
+ * $Id: Sanity.h,v 1.3 1999/01/13 17:25:44 simonm Exp $
  *
  * Prototypes for functions in Sanity.c
  *
@@ -7,9 +7,10 @@
 
 #ifdef DEBUG
 /* debugging routines */
-extern void checkHeap  ( bdescr *bd, nat step );
+extern void checkHeap  ( bdescr *bd, StgPtr start );
+extern void checkChain ( bdescr *bd );
 extern void checkStack ( StgPtr sp, StgPtr stack_end, StgUpdateFrame* su );
-extern void checkTSO   ( StgTSO* tso, nat step );
+extern void checkTSO   ( StgTSO* tso );
 
 extern StgOffset checkClosure( StgClosure* p );
 
index cade908..d3af459 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Schedule.c,v 1.3 1999/01/06 11:44:44 simonm Exp $
+ * $Id: Schedule.c,v 1.4 1999/01/13 17:25:44 simonm Exp $
  *
  * Scheduler
  *
@@ -119,7 +119,7 @@ initThread(StgTSO *tso, nat stack_size)
   SET_HDR((StgClosure*)tso->sp,(StgInfoTable *)&stg_stop_thread_info,CCS_MAIN);
   tso->su = (StgUpdateFrame*)tso->sp;
 
-  IF_DEBUG(scheduler,belch("Initialised thread %lld, stack size = %lx words\n", 
+  IF_DEBUG(scheduler,belch("Initialised thread %ld, stack size = %lx words\n", 
                           tso->id, tso->stack_size));
 
   /* Put the new thread on the head of the runnable queue.
@@ -160,7 +160,7 @@ void deleteThread(StgTSO *tso)
       return;
     }
 
-    IF_DEBUG(scheduler, belch("Killing thread %lld.", tso->id));
+    IF_DEBUG(scheduler, belch("Killing thread %ld.", tso->id));
 
     tso->whatNext = ThreadKilled; /* changed to ThreadComplete in schedule() */
     tso->link = END_TSO_QUEUE; /* Just to be on the safe side... */
@@ -363,7 +363,7 @@ SchedulerStatus schedule(StgTSO *main, StgClosure **ret_val)
     ccalling_threads = CurrentTSO;
     in_ccall_gc = rtsTrue;
     IF_DEBUG(scheduler,
-            fprintf(stderr, "Re-entry, thread %lld did a _ccall_gc\n", 
+            fprintf(stderr, "Re-entry, thread %d did a _ccall_gc\n", 
                     CurrentTSO->id););
   } else {
     in_ccall_gc = rtsFalse;
@@ -391,7 +391,12 @@ SchedulerStatus schedule(StgTSO *main, StgClosure **ret_val)
     } else {
       context_switch = 0;
     }
-    IF_DEBUG(scheduler, belch("Running thread %lld...\n", t->id));
+    IF_DEBUG(scheduler, belch("Running thread %ld...\n", t->id));
+
+    /* Be friendly to the storage manager: we're about to *run* this
+     * thread, so we better make sure the TSO is mutable.
+     */
+    recordMutable((StgMutClosure *)t);
 
     /* Run the current thread */
     switch (t->whatNext) {
@@ -441,14 +446,14 @@ SchedulerStatus schedule(StgTSO *main, StgClosure **ret_val)
     switch (ret) {
 
     case HeapOverflow:
-      IF_DEBUG(scheduler,belch("Thread %lld stopped: HeapOverflow\n", t->id));
+      IF_DEBUG(scheduler,belch("Thread %ld stopped: HeapOverflow\n", t->id));
       threadPaused(t);
       PUSH_ON_RUN_QUEUE(t);
       GarbageCollect(GetRoots);
       break;
 
     case StackOverflow:
-      IF_DEBUG(scheduler,belch("Thread %lld stopped, StackOverflow\n", t->id));
+      IF_DEBUG(scheduler,belch("Thread %ld stopped, StackOverflow\n", t->id));
       { 
        nat i;
        /* enlarge the stack */
@@ -474,9 +479,9 @@ SchedulerStatus schedule(StgTSO *main, StgClosure **ret_val)
                   /* ToDo: or maybe a timer expired when we were in Hugs?
                    * or maybe someone hit ctrl-C
                     */
-                   belch("Thread %lld stopped to switch to Hugs\n", t->id);
+                   belch("Thread %ld stopped to switch to Hugs\n", t->id);
                } else {
-                   belch("Thread %lld stopped, timer expired\n", t->id);
+                   belch("Thread %ld stopped, timer expired\n", t->id);
                }
                );
       threadPaused(t);
@@ -510,7 +515,7 @@ SchedulerStatus schedule(StgTSO *main, StgClosure **ret_val)
       break;
 
     case ThreadBlocked:
-      IF_DEBUG(scheduler,belch("Thread %lld stopped, blocking\n", t->id));
+      IF_DEBUG(scheduler,belch("Thread %ld stopped, blocking\n", t->id));
       threadPaused(t);
       /* assume the thread has put itself on some blocked queue
        * somewhere.
@@ -518,7 +523,7 @@ SchedulerStatus schedule(StgTSO *main, StgClosure **ret_val)
       break;
 
     case ThreadFinished:
-      IF_DEBUG(scheduler,belch("Thread %lld finished\n", t->id));
+      IF_DEBUG(scheduler,belch("Thread %ld finished\n", t->id));
       deleteThread(t);
       t->whatNext = ThreadComplete;
       break;
@@ -690,7 +695,14 @@ threadStackOverflow(StgTSO *tso)
   /* and relocate the update frame list */
   relocate_TSO(tso, dest);
 
-  IF_DEBUG(sanity,checkTSO(tso,0)); /* Step 0 because we're not GC'ing. */
+  /* Mark the old one as dead so we don't try to scavenge it during
+   * garbage collection (the TSO will likely be on a mutables list in
+   * some generation, but it'll get collected soon enough).
+   */
+  tso->whatNext = ThreadKilled;
+  dest->mut_link = NULL;
+
+  IF_DEBUG(sanity,checkTSO(tso));
 #if 0
   IF_DEBUG(scheduler,printTSO(dest));
 #endif
@@ -714,7 +726,7 @@ void awaken_blocked_queue(StgTSO *q)
     tso = q;
     q = tso->link;
     PUSH_ON_RUN_QUEUE(tso);
-    IF_DEBUG(scheduler,belch("Waking up thread %lld", tso->id));
+    IF_DEBUG(scheduler,belch("Waking up thread %ld", tso->id));
   }
 }
 
index b4421ff..1cbc0ba 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Stats.c,v 1.2 1998/12/02 13:28:49 simonm Exp $
+ * $Id: Stats.c,v 1.3 1999/01/13 17:25:46 simonm Exp $
  *
  * Statistics and timing-related functions.
  *
@@ -10,6 +10,8 @@
 #include "Rts.h"
 #include "RtsFlags.h"
 #include "RtsUtils.h"
+#include "StoragePriv.h"
+#include "MBlock.h"
 
 /**
  *  Ian: For the moment we just want to ignore
@@ -85,14 +87,6 @@ static ullong GC_tot_alloc = 0;
 static StgDouble GC_start_time,  GC_tot_time = 0;  /* User GC Time */
 static StgDouble GCe_start_time, GCe_tot_time = 0; /* Elapsed GC time */
 
-static StgDouble GC_min_time = 0;
-static StgDouble GCe_min_time = 0;
-static lnat GC_maj_no = 0;
-static lnat GC_min_no = 0;
-static lnat GC_min_since_maj = 0;
-static lnat GC_live_maj = 0;         /* Heap live at last major collection */
-static lnat GC_alloc_since_maj = 0;  /* Heap alloc since collection major */
-
 lnat MaxResidency = 0;     /* in words; for stats only */
 lnat ResidencySamples = 0; /* for stats only */
 
@@ -185,8 +179,8 @@ initStats(void)
   FILE *sf = RtsFlags.GcFlags.statsFile;
   
   if (RtsFlags.GcFlags.giveStats) {
-    fprintf(sf, "  Alloc  Collect   Live   Resid   GC    GC     TOT     TOT  Page Flts\n");
-    fprintf(sf, "  bytes   bytes    bytes   ency  user  elap    user    elap\n");
+    fprintf(sf, "    Alloc    Collect    Live    GC    GC     TOT     TOT  Page Flts\n");
+    fprintf(sf, "    bytes     bytes     bytes  user  elap    user    elap\n");
   }
 }    
 
@@ -265,7 +259,7 @@ stat_startGC(void)
    -------------------------------------------------------------------------- */
 
 void
-stat_endGC(lnat alloc, lnat collect, lnat live, char *comment)
+stat_endGC(lnat alloc, lnat collect, lnat live, lnat gen)
 {
     FILE *sf = RtsFlags.GcFlags.statsFile;
 
@@ -276,25 +270,31 @@ stat_endGC(lnat alloc, lnat collect, lnat live, char *comment)
        if (RtsFlags.GcFlags.giveStats) {
            nat faults = pagefaults();
 
-           fprintf(sf, "%8ld %7ld %7ld %5.1f%%",
-                   alloc*sizeof(W_), collect*sizeof(W_), live*sizeof(W_), collect == 0 ? 0.0 : (live / (StgDouble) collect * 100));
-           fprintf(sf, " %5.2f %5.2f %7.2f %7.2f %4ld %4ld  %s\n", 
+           fprintf(sf, "%9ld %9ld %9ld",
+                   alloc*sizeof(W_), collect*sizeof(W_), live*sizeof(W_));
+           fprintf(sf, " %5.2f %5.2f %7.2f %7.2f %4ld %4ld  (Gen: %2ld)\n", 
                    (time-GC_start_time), 
                    (etime-GCe_start_time), 
                    time,
                    etime,
                    faults - GC_start_faults,
                    GC_start_faults - GC_end_faults,
-                   comment);
+                   gen);
 
            GC_end_faults = faults;
            fflush(sf);
        }
 
-       GC_maj_no++;
        GC_tot_alloc += (ullong) alloc;
        GC_tot_time  += time-GC_start_time;
        GCe_tot_time += etime-GCe_start_time;
+
+       if (gen == RtsFlags.GcFlags.generations-1) { /* major GC? */
+         if (live > MaxResidency) {
+           MaxResidency = live;
+         }
+         ResidencySamples++;
+       }
     }
 
     if (rub_bell) {
@@ -327,31 +327,32 @@ stat_exit(int alloc)
        if (etime == 0.0) etime = 0.0001;
        
 
-       if (RtsFlags.GcFlags.giveStats) {
-           fprintf(sf, "%8d\n\n", alloc*sizeof(W_));
-       }
+       fprintf(sf, "%9ld %9.9s %9.9s",
+               (lnat)alloc*sizeof(W_), "", "");
+       fprintf(sf, " %5.2f %5.2f\n\n", 0.0, 0.0);
+
+       GC_tot_alloc += alloc;
 
-       else {
-           fprintf(sf, "%8ld %7.7s %6.6s %7.7s %6.6s",
-                   (GC_alloc_since_maj + alloc)*sizeof(W_), "", "", "", "");
-           fprintf(sf, "  %3ld  %5.2f %5.2f\n\n",
-                   GC_min_since_maj, GC_min_time, GCe_min_time);
-       }
-       GC_min_no    += GC_min_since_maj;
-       GC_tot_time  += GC_min_time;
-       GCe_tot_time += GCe_min_time;
-       GC_tot_alloc += GC_alloc_since_maj + alloc;
        ullong_format_string(GC_tot_alloc*sizeof(W_), temp, rtsTrue/*commas*/);
        fprintf(sf, "%11s bytes allocated in the heap\n", temp);
+
        if ( ResidencySamples > 0 ) {
            ullong_format_string(MaxResidency*sizeof(W_), temp, rtsTrue/*commas*/);
-           fprintf(sf, "%11s bytes maximum residency (%.1f%%, %ld sample(s))\n",
+           fprintf(sf, "%11s bytes maximum residency (%ld sample(s))\n",
                              temp,
-                             MaxResidency / (StgDouble) RtsFlags.GcFlags.maxHeapSize * 100,
                              ResidencySamples);
        }
-       fprintf(sf, "%11ld garbage collections performed (%ld major, %ld minor)\n\n",
-               GC_maj_no + GC_min_no, GC_maj_no, GC_min_no);
+       fprintf(sf,"\n");
+
+       { /* Count garbage collections */
+         nat g;
+         for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+           fprintf(sf, "%11d collections in generation %d\n", 
+                   generations[g].collections, g);
+         }
+       }
+       fprintf(sf,"\n%11ld Mb total memory in use\n\n", 
+               mblocks_allocated * MBLOCK_SIZE / (1024 * 1024));
 
        MutTime = time - GC_tot_time - InitUserTime;
        if (MutTime < 0) { MutTime = 0; }
@@ -386,3 +387,43 @@ stat_exit(int alloc)
        fclose(sf);
     }
 }
+
+/* -----------------------------------------------------------------------------
+   stat_describe_gens
+
+   Produce some detailed info on the state of the generational GC.
+   -------------------------------------------------------------------------- */
+void
+stat_describe_gens(void)
+{
+  nat g, s, mut, lge, live;
+  StgMutClosure *m;
+  bdescr *bd;
+  step *step;
+
+  fprintf(stderr, "     Gen    Steps      Max   Mutable   Step  Blocks     Live    Large\n"       "                    Blocks  Closures                          Objects\n");
+
+  for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+    for (m = generations[g].mut_list, mut = 0; m != END_MUT_LIST; 
+        m = m->mut_link) 
+      mut++;
+    fprintf(stderr, "%8d %8d %8d %9d", g, generations[g].n_steps,
+           generations[g].max_blocks, mut);
+
+    for (s = 0; s < generations[g].n_steps; s++) {
+      step = &generations[g].steps[s];
+      for (bd = step->large_objects, lge = 0; bd; bd = bd->link)
+       lge++;
+      live = 0;
+      for (bd = step->blocks; bd; bd = bd->link) {
+       live += (bd->free - bd->start) * sizeof(W_);
+      }
+      if (s != 0) {
+       fprintf(stderr,"%36s","");
+      }
+      fprintf(stderr,"%6d %8d %8d %8d\n", s, step->n_blocks,
+             live, lge);
+    }
+  }
+  fprintf(stderr,"\n");
+}
index be95442..2b9c0a5 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Stats.h,v 1.2 1998/12/02 13:28:50 simonm Exp $
+ * $Id: Stats.h,v 1.3 1999/01/13 17:25:46 simonm Exp $
  *
  * Statistics and timing-related functions.
  *
@@ -11,6 +11,6 @@ extern StgDouble usertime(void);
 extern void      end_init(void);
 extern void      stat_exit(int alloc);
 extern void      stat_startGC(void);
-extern void      stat_endGC(lnat alloc, lnat collect, lnat live, 
-                           char *comment);
+extern void      stat_endGC(lnat alloc, lnat collect, lnat live, lnat gen);
 extern void      initStats(void);
+extern void      stat_describe_gens(void);
index 227b27d..3e8cd99 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: StgMiscClosures.hc,v 1.2 1998/12/02 13:28:52 simonm Exp $
+ * $Id: StgMiscClosures.hc,v 1.3 1999/01/13 17:25:46 simonm Exp $
  *
  * Entry code for various built-in closure types.
  *
@@ -64,7 +64,7 @@ ling */
     FE_
 }  
 
-INFO_TABLE(IND_OLDGEN_info,IND_OLDGEN_entry,1,0,IND_OLDGEN,const,EF_,0,0);
+INFO_TABLE(IND_OLDGEN_info,IND_OLDGEN_entry,1,1,IND_OLDGEN,const,EF_,0,0);
 STGFUN(IND_OLDGEN_entry)
 {
     FB_
@@ -76,7 +76,7 @@ STGFUN(IND_OLDGEN_entry)
     FE_
 }
 
-INFO_TABLE(IND_OLDGEN_PERM_info,IND_OLDGEN_PERM_entry,1,0,IND_OLDGEN_PERM,const,EF_,0,0);
+INFO_TABLE(IND_OLDGEN_PERM_info,IND_OLDGEN_PERM_entry,1,1,IND_OLDGEN_PERM,const,EF_,0,0);
 STGFUN(IND_OLDGEN_PERM_entry)
 {
     FB_
@@ -127,12 +127,12 @@ STGFUN(CAF_ENTERED_entry)
    -------------------------------------------------------------------------- */
 
 /* Note: a black hole must be big enough to be overwritten with an
- * indirection/evacuee/catch.  Thus we claim it has 2 non-pointer words of
- * payload, which should be big enough for an old-generation
- * indirection.  
+ * indirection/evacuee/catch.  Thus we claim it has 1 non-pointer word of
+ * payload (in addition to the pointer word for the blocking queue), which 
+ * should be big enough for an old-generation indirection.  
  */
 
-INFO_TABLE(BLACKHOLE_info, BLACKHOLE_entry,0,2,BLACKHOLE,const,EF_,0,0);
+INFO_TABLE(BLACKHOLE_info, BLACKHOLE_entry,1,1,BLACKHOLE,const,EF_,0,0);
 STGFUN(BLACKHOLE_entry)
 {
   FB_
@@ -146,7 +146,7 @@ STGFUN(BLACKHOLE_entry)
 }
 
 /* identical to BLACKHOLEs except for the infotag */
-INFO_TABLE(CAF_BLACKHOLE_info, CAF_BLACKHOLE_entry,0,2,CAF_BLACKHOLE,const,EF_,0,0);
+INFO_TABLE(CAF_BLACKHOLE_info, CAF_BLACKHOLE_entry,1,1,CAF_BLACKHOLE,const,EF_,0,0);
 STGFUN(CAF_BLACKHOLE_entry)
 {
   FB_
@@ -226,10 +226,10 @@ NON_ENTERABLE_ENTRY_CODE(FOREIGN);
    and entry code for each type.
    -------------------------------------------------------------------------- */
 
-INFO_TABLE(FULL_MVAR_info,FULL_MVAR_entry,3,0,MVAR,const,EF_,0,0);
+INFO_TABLE(FULL_MVAR_info,FULL_MVAR_entry,4,0,MVAR,const,EF_,0,0);
 NON_ENTERABLE_ENTRY_CODE(FULL_MVAR);
 
-INFO_TABLE(EMPTY_MVAR_info,EMPTY_MVAR_entry,3,0,MVAR,const,EF_,0,0);
+INFO_TABLE(EMPTY_MVAR_info,EMPTY_MVAR_entry,4,0,MVAR,const,EF_,0,0);
 NON_ENTERABLE_ENTRY_CODE(EMPTY_MVAR);
 
 /* -----------------------------------------------------------------------------
@@ -246,6 +246,23 @@ SET_STATIC_HDR(END_TSO_QUEUE_closure,END_TSO_QUEUE_info,0/*CC*/,const,EI_)
 };
 
 /* -----------------------------------------------------------------------------
+   Mutable lists
+
+   Mutable lists (used by the garbage collector) consist of a chain of
+   StgMutClosures connected through their mut_link fields, ending in
+   an END_MUT_LIST closure.
+   -------------------------------------------------------------------------- */
+
+INFO_TABLE_CONSTR(END_MUT_LIST_info,END_MUT_LIST_entry,0,0,0,CONSTR_NOCAF_STATIC,const,EF_,0,0);
+NON_ENTERABLE_ENTRY_CODE(END_MUT_LIST);
+
+SET_STATIC_HDR(END_MUT_LIST_closure,END_MUT_LIST_info,0/*CC*/,const,EI_)
+};
+
+INFO_TABLE(MUT_CONS_info, MUT_CONS_entry, 1, 1, MUT_VAR, const, EF_, 0, 0);
+NON_ENTERABLE_ENTRY_CODE(MUT_CONS);
+
+/* -----------------------------------------------------------------------------
    Arrays
 
    These come in two basic flavours: arrays of data (StgArrWords) and arrays of
@@ -266,7 +283,6 @@ NON_ENTERABLE_ENTRY_CODE(type);
 
 ArrayInfo(ARR_WORDS);
 ArrayInfo(MUT_ARR_WORDS);
-ArrayInfo(ARR_PTRS);
 ArrayInfo(MUT_ARR_PTRS);
 ArrayInfo(MUT_ARR_PTRS_FROZEN);
 
@@ -276,7 +292,7 @@ ArrayInfo(MUT_ARR_PTRS_FROZEN);
    Mutable Variables
    -------------------------------------------------------------------------- */
 
-INFO_TABLE(MUT_VAR_info, MUT_VAR_entry, 1, 0, MUT_VAR, const, EF_, 0, 0);
+INFO_TABLE(MUT_VAR_info, MUT_VAR_entry, 1, 1, MUT_VAR, const, EF_, 0, 0);
 NON_ENTERABLE_ENTRY_CODE(MUT_VAR);
 
 /* -----------------------------------------------------------------------------
index e08ba9b..3d7a0b7 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Storage.c,v 1.2 1998/12/02 13:28:57 simonm Exp $
+ * $Id: Storage.c,v 1.3 1999/01/13 17:25:47 simonm Exp $
  *
  * Storage manager front end
  *
 #include "Stats.h"
 #include "Hooks.h"
 #include "BlockAlloc.h"
+#include "MBlock.h"
 #include "gmp.h"
 #include "Weak.h"
 
 #include "Storage.h"
 #include "StoragePriv.h"
 
-bdescr *nursery;               /* chained-blocks in the nursery */
 bdescr *current_nursery;       /* next available nursery block, or NULL */
 nat nursery_blocks;            /* number of blocks in the nursery */
 
@@ -31,9 +31,15 @@ nat alloc_blocks_lim;                /* approximate limit on alloc_blocks */
 StgPtr alloc_Hp    = NULL;     /* next free byte in small_alloc_list */
 StgPtr alloc_HpLim = NULL;     /* end of block at small_alloc_list   */
 
+generation *generations;       /* all the generations */
+generation *g0;                        /* generation 0, for convenience */
+generation *oldest_gen;                /* oldest generation, for convenience */
+step *g0s0;                    /* generation 0, step 0, for convenience */
+
 /*
  * Forward references
  */
+static bdescr *allocNursery   (nat blocks);
 static void *stgAllocForGMP   (size_t size_in_bytes);
 static void *stgReallocForGMP (void *ptr, size_t old_size, size_t new_size);
 static void  stgDeallocForGMP (void *ptr, size_t size);
@@ -41,9 +47,83 @@ static void  stgDeallocForGMP (void *ptr, size_t size);
 void
 initStorage (void)
 {
+  nat g, s;
+  step *step;
+
   initBlockAllocator();
   
-  nursery = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize);
+  /* allocate generation info array */
+  generations = (generation *)stgMallocBytes(RtsFlags.GcFlags.generations 
+                                            * sizeof(struct _generation),
+                                            "initStorage: gens");
+
+  /* set up all generations */
+  for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
+    generations[g].no = g;
+    generations[g].mut_list = END_MUT_LIST;
+    generations[g].collections = 0;
+    generations[g].failed_promotions = 0;
+  }
+
+  /* Oldest generation: one step */
+  g = RtsFlags.GcFlags.generations-1;
+  generations[g].n_steps = 1;
+  generations[g].steps = 
+    stgMallocBytes(1 * sizeof(struct _step), "initStorage: last step");
+  generations[g].max_blocks = RtsFlags.GcFlags.minAllocAreaSize * 4;
+  step = &generations[g].steps[0];
+  step->no = 0;
+  step->gen = &generations[g];
+  step->blocks = NULL;
+  step->n_blocks = 0;
+  step->to = step;             /* destination is this step */
+  step->hp = NULL;
+  step->hpLim = NULL;
+  step->hp_bd = NULL;
+  
+  /* set up all except the oldest generation with 2 steps */
+  for(g = 0; g < RtsFlags.GcFlags.generations-1; g++) {
+    generations[g].n_steps = 2;
+    generations[g].steps  = stgMallocBytes (2 * sizeof(struct _step),
+                                           "initStorage: steps");
+    generations[g].max_blocks = RtsFlags.GcFlags.minAllocAreaSize * 4;
+  }
+
+  for (g = 0; g < RtsFlags.GcFlags.generations-1; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      step = &generations[g].steps[s];
+      step->no = s;
+      step->blocks = NULL;
+      step->n_blocks = 0;
+      step->gen = &generations[g];
+      if ( s == 1 ) {
+       step->to = &generations[g+1].steps[0];
+      } else {
+       step->to = &generations[g].steps[s+1];
+      }
+      step->hp = NULL;
+      step->hpLim = NULL;
+      step->hp_bd = NULL;
+      step->large_objects = NULL;
+      step->new_large_objects = NULL;
+      step->scavenged_large_objects = NULL;
+    }
+  }
+  
+  oldest_gen = &generations[RtsFlags.GcFlags.generations-1];
+
+  /* generation 0 is special: that's the nursery */
+  g0 = &generations[0];
+  generations[0].max_blocks = 0;
+
+  /* G0S0: the allocation area */
+  step = &generations[0].steps[0];
+  g0s0 = step;
+  step->blocks   = allocNursery(RtsFlags.GcFlags.minAllocAreaSize);
+  step->n_blocks = RtsFlags.GcFlags.minAllocAreaSize;
+  nursery_blocks = RtsFlags.GcFlags.minAllocAreaSize;
+  current_nursery = step->blocks;
+  /* hp, hpLim, hp_bd, to_space etc. aren't used in G0S0 */
 
   weak_ptr_list = NULL;
   caf_list = NULL;
@@ -58,24 +138,27 @@ initStorage (void)
   /* Tell GNU multi-precision pkg about our custom alloc functions */
   mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP);
 #endif
+
+  IF_DEBUG(gc, stat_describe_gens());
 }
 
-bdescr *
-allocNursery (bdescr *last_bd, nat blocks)
+static bdescr *
+allocNursery (nat blocks)
 {
-  bdescr *bd;
+  bdescr *last_bd, *bd;
   nat i;
 
+  last_bd = NULL;
   /* Allocate a nursery */
   for (i=0; i < blocks; i++) {
     bd = allocBlock();
     bd->link = last_bd;
-    bd->step = 0;
+    bd->step = g0s0;
+    bd->gen = g0;
+    bd->evacuated = 0;
     bd->free = bd->start;
     last_bd = bd;
   }
-  nursery_blocks = blocks;
-  current_nursery = last_bd;
   return last_bd;
 }
 
@@ -95,13 +178,46 @@ exitStorage (void)
 }
 
 void
+recordMutable(StgMutClosure *p)
+{
+  bdescr *bd;
+
+  ASSERT(closure_MUTABLE(p));
+
+  bd = Bdescr((P_)p);
+
+  /* no need to bother in generation 0 */
+  if (bd->gen == g0) { 
+    return; 
+  } 
+
+  if (p->mut_link == NULL) {
+    p->mut_link = bd->gen->mut_list;
+    bd->gen->mut_list = p;
+  }
+}
+
+void
 newCAF(StgClosure* caf)
 {
-  const StgInfoTable *info = get_itbl(caf);
+  const StgInfoTable *info;
+
+  /* Put this CAF on the mutable list for the old generation.
+   * This is a HACK - the IND_STATIC closure doesn't really have
+   * a mut_link field, but we pretend it has - in fact we re-use
+   * the STATIC_LINK field for the time being, because when we
+   * come to do a major GC we won't need the mut_link field
+   * any more and can use it as a STATIC_LINK.
+   */
+  ((StgMutClosure *)caf)->mut_link = oldest_gen->mut_list;
+  oldest_gen->mut_list = (StgMutClosure *)caf;
 
+#ifdef DEBUG
+  info = get_itbl(caf);
   ASSERT(info->type == IND_STATIC);
   STATIC_LINK2(info,caf) = caf_list;
   caf_list = caf;
+#endif
 }
 
 /* -----------------------------------------------------------------------------
@@ -122,16 +238,15 @@ allocate(nat n)
   CCS_ALLOC(CCCS,n);
 
   /* big allocation (>LARGE_OBJECT_THRESHOLD) */
+  /* ToDo: allocate directly into generation 1 */
   if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
     nat req_blocks =  (lnat)BLOCK_ROUND_UP(n*sizeof(W_)) / BLOCK_SIZE;
     bd = allocGroup(req_blocks);
-    bd->link = large_alloc_list; 
-    bd->back = NULL;
-    if (large_alloc_list) {
-      large_alloc_list->back = bd; /* double-link the list */
-    }
-    large_alloc_list = bd;
-    bd->step = 0;
+    dbl_link_onto(bd, &g0s0->large_objects);
+    bd->gen  = g0;
+    bd->step = g0s0;
+    bd->evacuated = 0;
+    bd->free = bd->start;
     /* don't add these blocks to alloc_blocks, since we're assuming
      * that large objects are likely to remain live for quite a while
      * (eg. running threads), so garbage collecting early won't make
@@ -147,7 +262,9 @@ allocate(nat n)
     bd = allocBlock();
     bd->link = small_alloc_list;
     small_alloc_list = bd;
-    bd->step = 0;
+    bd->gen = g0;
+    bd->step = g0s0;
+    bd->evacuated = 0;
     alloc_Hp = bd->start;
     alloc_HpLim = bd->start + BLOCK_SIZE_W;
     alloc_blocks++;
@@ -215,3 +332,65 @@ stgDeallocForGMP (void *ptr STG_UNUSED,
 {
     /* easy for us: the garbage collector does the dealloc'n */
 }
+
+/* -----------------------------------------------------------------------------
+   Debugging
+
+   memInventory() checks for memory leaks by counting up all the
+   blocks we know about and comparing that to the number of blocks
+   allegedly floating around in the system.
+   -------------------------------------------------------------------------- */
+
+#ifdef DEBUG
+
+extern void
+memInventory(void)
+{
+  nat g, s;
+  step *step;
+  bdescr *bd;
+  lnat total_blocks = 0, free_blocks = 0;
+
+  /* count the blocks we current have */
+  for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      step = &generations[g].steps[s];
+      total_blocks += step->n_blocks;
+      for (bd = step->large_objects; bd; bd = bd->link) {
+       total_blocks += bd->blocks;
+       /* hack for megablock groups: they have an extra block or two in
+          the second and subsequent megablocks where the block
+          descriptors would normally go.
+       */
+       if (bd->blocks > BLOCKS_PER_MBLOCK) {
+         total_blocks -= (MBLOCK_SIZE / BLOCK_SIZE - BLOCKS_PER_MBLOCK)
+                         * bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE);
+       }
+      }
+    }
+  }
+
+  /* any blocks held by allocate() */
+  for (bd = small_alloc_list; bd; bd = bd->link) {
+    total_blocks += bd->blocks;
+  }
+  for (bd = large_alloc_list; bd; bd = bd->link) {
+    total_blocks += bd->blocks;
+  }
+  
+  /* count the blocks on the free list */
+  free_blocks = countFreeList();
+
+  ASSERT(total_blocks + free_blocks == mblocks_allocated * BLOCKS_PER_MBLOCK);
+
+#if 0
+  if (total_blocks + free_blocks != mblocks_allocated *
+      BLOCKS_PER_MBLOCK) {
+    fprintf(stderr, "Blocks: %ld live + %ld free  = %ld total (%ld around)\n",
+           total_blocks, free_blocks, total_blocks + free_blocks,
+           mblocks_allocated * BLOCKS_PER_MBLOCK);
+  }
+#endif
+}
+
+#endif
index b11e8aa..d197087 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Storage.h,v 1.2 1998/12/02 13:28:58 simonm Exp $
+ * $Id: Storage.h,v 1.3 1999/01/13 17:25:48 simonm Exp $
  *
  * External Storage Manger Interface
  *
@@ -87,8 +87,30 @@ extern StgClosure *MarkRoot(StgClosure *p);
 
    -------------------------------------------------------------------------- */
 
-extern void RecordMutable(StgPtr p);
-extern void UpdateWithIndirection(StgPtr p1, StgPtr p2);
+extern void recordMutable(StgMutClosure *p);
+
+#ifdef TICKY_TICKY
+#error updateWithIndirection: maybe permanent indirection?
+# define Ind_info_TO_USE ((AllFlags.doUpdEntryCounts) ? &IND_PERM_info : &IND_info
+)
+#endif
+
+static inline void
+updateWithIndirection(StgClosure *p1, StgClosure *p2) 
+{
+  bdescr *bd;
+
+  bd = Bdescr((P_)p1);
+  if (bd->gen->no == 0) {
+    SET_INFO(p1,&IND_info);
+    ((StgInd *)p1)->indirectee = p2;
+  } else {
+    SET_INFO(p1,&IND_OLDGEN_info);
+    ((StgIndOldGen *)p1)->indirectee = p2;
+    ((StgIndOldGen *)p1)->mut_link = bd->gen->mut_list;
+    bd->gen->mut_list = (StgMutClosure *)p1;
+  }
+}
 
 /* -----------------------------------------------------------------------------
    The CAF list - used to let us revert CAFs
index c3054a5..8231865 100644 (file)
@@ -1,11 +1,96 @@
 /* -----------------------------------------------------------------------------
- * $Id: StoragePriv.h,v 1.2 1998/12/02 13:28:59 simonm Exp $
+ * $Id: StoragePriv.h,v 1.3 1999/01/13 17:25:48 simonm Exp $
  *
  * Internal Storage Manger Interface
  *
  * ---------------------------------------------------------------------------*/
 
-extern bdescr *allocNursery (bdescr *last_bd, nat blocks);
+#ifndef STORAGEPRIV_H
+#define STORAGEPRIV_H
+
+/* GENERATION GC NOTES
+ *
+ * We support an arbitrary number of generations, with an arbitrary number
+ * of steps per generation.  Notes (in no particular order):
+ *
+ *       - all generations except the oldest should have two steps.  This gives
+ *         objects a decent chance to age before being promoted, and in
+ *         particular will ensure that we don't end up with too many
+ *         thunks being updated in older generations.
+ *
+ *       - the oldest generation has one step.  There's no point in aging
+ *         objects in the oldest generation.
+ *
+ *       - generation 0, step 0 (G0S0) is the allocation area.  It is given
+ *         a fixed set of blocks during initialisation, and these blocks
+ *         are never freed.
+ *
+ *       - during garbage collection, each step which is an evacuation
+ *         destination (i.e. all steps except G0S0) is allocated a to-space.
+ *         evacuated objects are allocated into the step's to-space until
+ *         GC is finished, when the original step's contents may be freed
+ *         and replaced by the to-space.
+ *
+ *       - the mutable-list is per-generation (not per-step).  G0 doesn't 
+ *         have one (since every garbage collection collects at least G0).
+ * 
+ *       - block descriptors contain pointers to both the step and the
+ *         generation that the block belongs to, for convenience.
+ *
+ *       - static objects are stored in per-generation lists.  See GC.c for
+ *         details of how we collect CAFs in the generational scheme.
+ *
+ *       - large objects are per-step, and are promoted in the same way
+ *         as small objects, except that we may allocate large objects into
+ *         generation 1 initially.
+ */
+
+typedef struct _step {
+  nat no;                      /* step number */
+  bdescr *blocks;              /* blocks in this step */
+  nat n_blocks;                        /* number of blocks */
+  struct _step *to;            /* where collected objects from this step go */
+  struct _generation *gen;     /* generation this step belongs to */
+  bdescr *large_objects;       /* large objects (doubly linked) */
+
+  /* temporary use during GC: */
+  StgPtr  hp;                  /* next free locn in to-space */
+  StgPtr  hpLim;               /* end of current to-space block */
+  bdescr *hp_bd;               /* bdescr of current to-space block */
+  bdescr *to_space;            /* bdescr of first to-space block */
+  nat     to_blocks;           /* number of blocks in to-space */
+  bdescr *scan_bd;             /* block currently being scanned */
+  StgPtr  scan;                        /* scan pointer in current block */
+  bdescr *new_large_objects;    /* large objects collected so far */
+  bdescr *scavenged_large_objects; /* live large objects after GC (dbl link) */
+
+#ifdef DEBUG
+  /* for sanity checking: */
+  bdescr *old_scan_bd;
+  StgPtr  old_scan;
+#endif
+} step;
+
+typedef struct _generation {
+  nat no;                      /* generation number */
+  step *steps;                 /* steps */
+  nat n_steps;                 /* number of steps */
+  nat max_blocks;              /* max blocks in step 0 */
+  StgMutClosure *mut_list;      /* mutable objects in this generation (not G0)*/
+
+  /* stats information */
+  nat collections;
+  nat failed_promotions;
+} generation;
+
+#define END_OF_STATIC_LIST stgCast(StgClosure*,1)
+
+extern generation *generations;
+
+extern generation *g0;
+extern step *g0s0;
+extern generation *oldest_gen;
+
 extern void newCAF(StgClosure*);
 extern StgTSO *relocate_TSO(StgTSO *src, StgTSO *dest);
 
@@ -24,3 +109,25 @@ extern nat nursery_blocks;
 extern nat alloc_blocks;
 extern nat alloc_blocks_lim;
 
+static inline void
+dbl_link_onto(bdescr *bd, bdescr **list)
+{
+  bd->link = *list;
+  bd->back = NULL;
+  if (*list) {
+    (*list)->back = bd; /* double-link the list */
+  }
+  *list = bd;
+}
+
+/* MUTABLE LISTS
+ * A mutable list is ended with END_MUT_LIST, so that we can use NULL
+ * as an indication that an object is not on a mutable list.
+ */
+#define END_MUT_LIST ((StgMutClosure *)(void *)&END_MUT_LIST_closure)
+
+#ifdef DEBUG
+extern void memInventory(void);
+#endif
+
+#endif /* STORAGEPRIV_H */
index e4359d2..012a88d 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Updates.hc,v 1.2 1998/12/02 13:29:00 simonm Exp $
+ * $Id: Updates.hc,v 1.3 1999/01/13 17:25:49 simonm Exp $
  *
  * Code to perform updates.
  *
@@ -8,6 +8,7 @@
 #include "Rts.h"
 #include "RtsUtils.h"
 #include "HeapStackCheck.h"
+#include "Storage.h"
 
 /*
   The update frame return address must be *polymorphic*, that means
@@ -51,7 +52,7 @@
          TICK_UPD_EXISTING();                                          \
                                                                        \
           updatee = ((StgUpdateFrame *)Sp)->updatee;                   \
-                                                                       \
+                                               \
          /* update the updatee with an indirection to the return value */\
          UPD_IND(updatee,R1.p);                                        \
                                                                        \
@@ -75,7 +76,6 @@ UPD_FRAME_ENTRY_TEMPLATE(Upd_frame_5_entry,RET_VEC(Sp[0],5));
 UPD_FRAME_ENTRY_TEMPLATE(Upd_frame_6_entry,RET_VEC(Sp[0],6));
 UPD_FRAME_ENTRY_TEMPLATE(Upd_frame_7_entry,RET_VEC(Sp[0],7));
 
-
 /*
   Make sure this table is big enough to handle the maximum vectored
   return size!
index db97ecc..9cd70eb 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Weak.c,v 1.2 1998/12/02 13:29:01 simonm Exp $
+ * $Id: Weak.c,v 1.3 1999/01/13 17:25:49 simonm Exp $
  *
  * Weak pointers / finalisers
  *
@@ -50,6 +50,12 @@ scheduleFinalisers(StgWeak *list)
     createIOThread(RtsFlags.GcFlags.initialStkSize, w->finaliser);
 #endif
     w->header.info = &DEAD_WEAK_info;
+
+    /* need to fill the slop with zeros if we're sanity checking */
+    IF_DEBUG(sanity, {
+      nat dw_size = sizeW_fromITBL(get_itbl(w));
+      memset((P_)w + dw_size, 0, (sizeofW(StgWeak) - dw_size) * sizeof(W_));
+    });
   }
 }