Commit 4391e44f authored by simonm's avatar simonm

[project @ 1999-01-13 17:25:37 by simonm]

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.
parent dc49719c
......@@ -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>
......
......@@ -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'
......
/* -----------------------------------------------------------------------------
* $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;
......
/* ----------------------------------------------------------------------------
* $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;
......
/* ----------------------------------------------------------------------------
* $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
*
......@@ -12,37 +12,29 @@
/* 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
......@@ -50,28 +42,23 @@
#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
/* ----------------------------------------------------------------------------
* $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;
......
/* ----------------------------------------------------------------------------
* $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;
......
/* -----------------------------------------------------------------------------
* $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: */
......
/* -----------------------------------------------------------------------------
* $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
*
......
/* -----------------------------------------------------------------------------
* $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[];
......
/* -----------------------------------------------------------------------------
* $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;
......
/* -----------------------------------------------------------------------------
* $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
......@@ -31,11 +19,16 @@
* (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*);
......
/* -----------------------------------------------------------------------------
* $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
/* -----------------------------------------------------------------------------
* $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