[project @ 2001-08-22 10:44:43 by gla]
authorgla <unknown>
Wed, 22 Aug 2001 10:44:43 +0000 (10:44 +0000)
committergla <unknown>
Wed, 22 Aug 2001 10:44:43 +0000 (10:44 +0000)
Reorganized all the sections and rewrote most of the contents.
Now describes the actual implementation, not the implementation strategy, which
was the case in the previous version.
Based on the GHC 5.01, and removed all obsolete parts.

Divides the storage manager into four parts: megablock allocator, block allocator,
heap allocator, and garbage collector.
Gives the details of the garbage collector, which should be sufficient for those
who want to analyze its implementation.

ghc/docs/storage-mgt/sm.tex

index 7c3af13..293068b 100644 (file)
@@ -1,5 +1,7 @@
 \documentclass{article}
-\usepackage{code}
+\usepackage{code,a4wide}
+
+\usepackage{graphics,epsfig,epic,eepic}
 
 \setlength{\parskip}{0.25cm}
 \setlength{\parsep}{0.25cm}
 \newcommand{\note}[1]{{\em $\spadesuit$ #1}}
 
 \begin{document}
-\title{The GHC storage manager}
-\author{Simon Peyton Jones}
+\title{The GHC Storage Manager}
+\author{Simon Peyton Jones and Sungwoo Park}
 
 \makeatactive
 \maketitle
-
 \section{Introduction}
 
-This is a draft design document, not a completed thing.
+This document describes the details of the GHC storage manager, including
+the interface and implementation of each of its components.
 
 \section{Goals}
 
-Storage management goals:
-
+Storage management goals are:
 \begin{itemize}
 \item Generational collection, supporting multiple generations.
-
 \item The ability to pin the allocation
 area into a few pages that we hope will fit entirely in the cache.
-
 \item Allows objects to age within a generation before getting promoted.
-
 \item Heap can grow as needed, rather than having to be pre-sized
   by the programmer.
-
 \item We support mark/sweep/compact collection for older generations.
 This is a Good Thing when the live memory approaches the available
 physical memory, because it reduces paging.
-
-\item Little OS support needed.  No mmap etc. All that we require is
-  the ability to @malloc@ (or whatever) a new chunk of memory.
+\item Little OS support needed.  No @mmap()@ etc. All that we require is
+  the ability to call @malloc()@ to allocate a new chunk of memory.
   There can be intervening ``sandbars'' allocated by other programs
-  (e.g. DLLs or other @malloc@'d structures) between chunks of heap.
-
-\item Possible feature: ability to support mostly-copying conservative 
-collection.[.bartlett mostly 1989, morrisset mostly.]
-Not top priority.
+  (e.g. DLLs or other @malloc()@'d structures) between chunks of heap.
 \end{itemize}
 
-Language-support goals:
+Language-support goals are:
 \begin{itemize}
 \item The garbage collector ``shorts out'' indirection objects introduced
 by the mutator (notably when overwriting a thunk with an indirection).
-
-\item The garbage collector executes selector thunks.  
+\item The garbage collector executes selector thunks.
 For example, a thunk for
 @(fst x)@ where @x@ is a pointer to a pair @(a,b)@ would be
 evaluated by the garbage collector to just @a@.  This is an important
 strategy for plugging space leaks.
-
 \item The garbage collector traversese the code tree, as well as
-the heap data structures, to find which CAFs are live.  This is a Royal Pain.
-
+the heap data structures, to find which CAFs are live. This is a royal pain.
 \item The garbage collector finalises some objects (typically a tiny minority).
 At the moment ``finalisation'' means ``call a C routine when this thing
-dies'' but it would be more general to schedule a call to a Haskell 
+dies'' but it would be more general to schedule a call to a Haskell
 procedure.
 \end{itemize}
 
-Instrumentation goals:
-
+Instrumentation goals are:
 \begin{itemize}
 \item The garbage collector can gather heap-census information for profiling.
 To this end we can force GC to happen more often than it otherwise would,
@@ -88,417 +76,877 @@ and the collector can gather information about the type and cost-centre
 associated with each heap object.
 \end{itemize}
 
+\section{The architecture of the storage manager}
+
+The storage manager is a component of the GHC system which is responsible
+for allocating fresh memory for new objects and reclaiming memory 
+that is no longer used.
+It is built on a layered architecture and consists of four main parts:
+\emph{megablock allocator}, \emph{block allocator}, \emph{heap allocator}, 
+and \emph{garbage collector} (Figure~\ref{fig-architecture}). 
+The megablock allocator communicates directly with the underlying 
+operating system and forms the lowest level of the storage manager.
+The heap allocator and garbage collector lie in the topmost level of
+the storage manager and process requests from 
+the mutator (the Haskell realm at the runtime) and the runtime system.
+The block allocator lies between the two levels. 
+
+\begin{figure}[ht]
+\begin{center}
+\input{architecture.eepic}
+\caption{The overall architecture of the storage manager}
+\label{fig-architecture}
+\end{center}
+\end{figure}
 
-\section{Assumptions}
-
-Every garbage collector uses assupmtions about the sort of
-objects it manipulates, and how the mutator behaves.  Here we
-collect our assumptions:
-\begin{itemize}
-\item Each heap object starts with a one-word header that encodes, or points
-to, information about the layout of the object.  The details of this
-encoding aren't fundamental to the collector, but are, of course, rather 
-important.
-
-\item Every heap pointer points to the beginning of
-an object; no pointers into the middle of objects.
-
-\item There are two spare bits in the LSB of a pointer, because
-every heap object is at least word aligned.
-
-\item
-Heap objects are packed contiguously, with no ``slop'' between them.
-This assumption makes it possible to scan a region of heap linearly.
-It's easy to arrange that objects are packed contiguously in the
-to-space of a copying collector, but the no-slop assumption also
-applies during mutation.  This is a bit of a pain, because we often
-update a thunk with an indirection to the thunk's value; and the
-indirection isn't necessarily the same size as the thunk.  This can be
-solved, but it takes a few more instructions.
-
-We know of two reasons to uphold the no-slop assumption:
-\begin{itemize}
-\item A {\em mostly-copying conservative (MCC) collector} has to deal with 
-possible pointers from conservative regions.[.morrisset mostly.]
-Given a possible pointer the MCC collector must find the head of the object into
-which the putative pointer points.  One easy way to do this is
-to scan forward from a known point; but only if there is no
-slop.
-\item An {\em incremental collector}
-will scan linearly during mutation.[.field while.]
-\end{itemize}
-\end{itemize}
-
-
-\section{\Block{}s}
+\section{The megablock allocator}
 
-The basic memory structure is similar to other BIBOP-based 
-collectors.[.stop the bibop, reppy garbage collector, moss garbage toolkit.]
+The megablock allocator requests a chunk of physical memory of a fixed size,
+which is called a \emph{megablock}, from the operating system and returns it
+to the block allocator. A new megablock is not initialized by the
+megablock allocator; it is later initialized by the block allocator.
 
-A {\em \block} is a contiguous chunk of $2^K$ bytes, starting on a 
-$2^K$-byte boundary.  We expect a \block{} to be between 4k and 64k bytes.
+\subsection{Interface}
 
-A \block{} is the unit of allocation for the storage manager.  That is,
-the {\em block allocator} hands over store to the mutator in multiples of
-one \block.  (The mutator may then allocate multiple heap objects within
-a \block, of course.)
+\begin{description}
+\item[@void *getMBlock()@] allocates a single megablock and returns its 
+starting address. @MBlock.c@.
+\item[@void *getMBlocks(nat n)@] allocates @n@ contiguous megablocks 
+and returns their starting address. @MBlock.c@.
+\end{description}
 
-\Block{}s are often allocated individually, but for large
-objects, or just to reduce inter-block linkage costs, a
-contiguous group of \block{}s can be allocated; we call
-this a {\em \block{} group} or sometimes just {\em group}.
-The first \block{} of a group is called the {\em group head}.
+\subsection{Implementation}
 
-\subsection{\Block{} descriptors}
+Since the megablock allocator communicates directly with the underlying
+operating system, its implementation relies on memory allocation functions
+provided by the operating system; thus, the implementation varies between
+platforms. 
+However, every megablock is always of a fixed size $2^K$ and aligned on a 
+$2^K$ boundary, regardless of the platform 
+(@MBLOCK_SIZE@ in @include/Constants.h@ defines the size of megablocks).
+@mblocks_allocated@ in @MBlock.c@ stores the number of megablocks allocated.
+
+For implementation details, see @MBlock.c@, @MBlock.h@, @include/Block.h@. 
+
+\section{The block allocator}
+
+The block allocator divides a megablock returned by the megablock allocator
+into a contiguous group of \emph{block descriptors} followed by another 
+contiguous group of \emph{blocks}. 
+
+A block is a contiguous chunk of $2^K$ bytes, starting on 
+a $2^K$-byte boundary (@BLOCK_SIZE@ in 
+@include/Constants.h@ defines the size of blocks). 
+Each block has its own associated block descriptor, which records the
+current state of the block. 
+Both blocks and block descriptors are placed in a common megablock.
+
+Figure~\ref{fig-megablock} shows a megablock after initialization by the 
+megablock allocator. 
+Block descriptors occupy the lower address space and blocks the higher address 
+space in the megablock. 
+Since block descriptors are ordered linearly, we can always locate a block 
+descriptor corresponding to a particular block from the starting address 
+of the block.
+
+\begin{figure}[ht]
+\begin{center}
+\input{megablock.eepic}
+\caption{A megablock after initialization}
+\label{fig-megablock}
+\end{center}
+\end{figure}
+
+A block is the unit of allocation for the block allocator. 
+That is, the block allocator hands over store to the heap allocator in multiples of 
+one block, where multiple heap objects may be allocated.
+A contiguous group of blocks, which is called a \emph{block group}, can be 
+directly handed over to the heap allocator to reduce inter-block 
+linkage costs.
+The first block of a block group is called the \emph{group head}.\footnote{
+An alternative design has the block descriptor at the start of each block.
+This makes it easy to locate the block descriptor corresponding to a particular
+block, but is pessimistic for cache locality when fiddling with block descriptors.
+It also means that only the first block in a contiguous chunk of blocks can
+have a block descriptor. This in turn makes it difficult to achieve an
+efficient mostly-copying conservative (MCC) garbage collector.}
+
+\subsection{Block descriptors}
+
+A block descriptor has the following structure, defined in 
+@include/Blocks.h@:
 
-Each block has an associated {\em block{} descriptor}, a
-record with the following structure:
 \begin{code}
-  typedef struct {
-       int Gen;
-       int Step;
-       void *Start;
-       void *Free;
-       bdescr *Link
-    } bdescr;
+typedef struct _bdescr {
+  StgPtr          start;
+  StgPtr          free; 
+  StgWord32       blocks;
+  struct _bdescr  *link;
+  /* additional fields */
+} bdescr;
 \end{code}
-The function @BDescr(a)@ takes an address @a@ and returns
-the address of its block descriptor.  It is expected that 
-@BDescr@ only takes a few instructions (see below).
-The field of a \block{} descriptor have the following 
-purposes:
-\begin{center}
-\begin{tabular}{lp{4.5in}}
-@Gen@ & The generation to which this block belongs.  The 
-youngest generation is 0. Valid for all blocks whether or not
-it is a group head.
-\\ \\
-@Step@ & The number of GCs that this block has survived
-while within its current generation. Only valid for group heads.
-\\ \\
-@Start@ & Points to the first byte of the block itself. 
-Valid for all blocks.
-\\ \\
-@Free@ & For a group head, @Free@ points to the first free (un-allocated) 
-byte in the group. For non-group heads, @Free@ is set to zero;
-this identifies the \block{} as a non-head.
-\\ \\
-@Link@ & Used to chain \block{}s together.  For non-group-heads,
-@Link@ points to the (\block{} descriptor of the) group head.
-\end{tabular}
-\end{center}
 
-\subsection{\Step{}s}
+The fields of a block descriptor have the following purposes:
 
-A {\em \step} is a linked list of \block{} groups, {\em not} necessarily contiguous,
-all belonging to the same generation,
-and all of which have survived the same number of garbage collections.
+\begin{description}
+\item[@start@] points to the first byte of the corresponding block.
+\item[@free@] For a group head, @free@ points to the first free byte in 
+the block group. For a non-group head, @free@ is set to zero to identify
+the corresponding block as a non-group head.
+\item[@blocks@] For a group head, @blocks@ stores the number of blocks
+in the block group. It is not used for non-group heads.
+\item[@link@] For a group head, @link@ is used to chain all individual 
+blocks or block groups together. For a non-group head, @link@ points
+to the block descriptor of the group head.
+\end{description}
 
-We think of a \step{} as {\em logically} contiguous (via their @Link@ fields) even though
-it is not physically contiguous.
+\subsection{Interface}
 
-The \step{} number of a \step{} indicates its age (in GCs) within its generation.
-(\Step{} zero is the youngest \step{} in a generation.)  During GC of generation G, 
-live objects
-from \step{} N are moved to \step{} N+1, and live objects from \step{} @MaxStep@(G)
-are promoted to generation G+1. In this way, objects are given a decent chance
-to die before being promoted.  
+\begin{description}
+\item[@void initBlockAllocator(void)@] initializes the block allocator. 
+@BlockAlloc.c@.
+\item[@bdescr *allocBlock(void)@] requests a single block and returns 
+the address of its block descriptor. @BlockAlloc.c@.
+\item[@bdescr *allocGroup(nat n)@] allocates a block group of size @n@ 
+and returns the address of the block descriptor for the group head.
+@BlockAlloc.c@.
+\item[@void freeGroup(bdescr *p)@] frees the block group where @p@ points
+to the block descriptor of the group head, and places it in a pool of
+free block groups. @BlockAlloc.c@.
+\item[@bdescr *Bdescr(StgPtr p)@] takes a pointer @p@ to any byte within
+a block and returns a pointer to its block descriptor. It is implemented as
+an @inline@ procedure. @include/Block.h@.
+\end{description}
 
-In effect, we record the age for a set of objects grouped by address (the \step{}), 
-rather than recording the age for each object individually.  This is quite
-a conventional idea.
+\subsection{Implementation}
 
-\subsection{Generations}
+The block allocator maintains a linked list of free block groups, whose head
+is stored in @free_list@ in @BlockAlloc.c@ (Figure~\ref{fig-freelist}).
+When @allocBlock()@ or @allocGroup()@ is called, the block allocator
+scans the linked list from @free_list@ and finds the first block group
+which can handle the request.
+If such a block group exists, it takes off the requested number of blocks
+from the block group, creates a new block group from them, 
+initializes it if needed, and returns it to the caller. 
+The rest of the old block group, if any, is linked back to the list of free block 
+groups as another block group. 
+If such a block group does not exist, the block allocator requests a megablock
+from the megablock allocator and processes the request using the new megablock.
+
+For implementation details, see @BlockAlloc.c@.
+
+\begin{figure}[ht]
+\begin{center}
+\input{freelist.eepic}
+\caption{Linked list of free block groups}
+\label{fig-freelist}
+\end{center}
+\end{figure}
+
+\section{Heap allocator}
+
+The role of the heap allocator in the storage manager is to allocate fresh
+memory upon requests from the mutator and the runtime system.
+Memory allocation takes place frequently during the execution of Haskell 
+programs, and hence its efficiency is crucial to the overall performance.
+To handle requests from the mutator and the runtime system efficiently,
+the heap allocator maintains three different memory stores,
+each of which has its own purpose.
+
+The first store is the \emph{nursery}, where typical Haskell 
+objects are born.
+The mutator itself can allocate fresh memory directly in the nursery 
+without invoking an interface function:
+the configuration of the nursery is always revealed to the mutator and can even
+be changed by the mutator when it allocates fresh memory from the nursery
+on its own.
+Thus, although the small overhead in manipulating the nursery results in fast
+memory allocation, it is up to the mutator to keep the nursery in an 
+uncorrupted state.
+
+The second and the third are the \emph{small object pool} and the
+\emph{large object pool}.
+The heap allocator provides a common interface function to be shared by both stores:
+the size of fresh memory requested, which is passed as an argument to the 
+interface function, determines which of the two stores to be used.
+The interface function can be called by both the mutator and the runtime system.
+
+\subsection{Interface}
+
+\begin{description}
+\item[@void allocNurseries(void)@] creates and initializes the nursery. 
+@Storage.c@.
+\item[@void resetNurseries(void)@] re-initializes the nursery. @Storage.c@.
+\item[@Hp@] points to the byte before the first byte of the current allocation area 
+(contiguous free bytes in the nursery). 
+Defined as @MainRegTable.rHp@ in @include/Regs.h@.
+\item[@HpLim@] marks the boundary of the current allocation area:
+it points to the last byte in the current allocation area, and thus
+all the bytes of memory addresses from @Hp@$ + 1$ to @HpLim@ are free.
+The mutator can obtain fresh memory simply by adjusting @Hp@ as long as the new
+value of @Hp@ does not exceed @HpLim@. For instance, if the mutator 
+increases @Hp@ by @n@, it can now store an object of size up to @n@ at the 
+address pointed to by the old value of @Hp@$ + 1$.
+Defined as @MainRegTable.rHpLim@ in @include/Regs.h@.\footnote{@HpLim@ is not always
+correct. It may point event to a word in the next block.}
+\item[@OpenNursery(hp, hplim)@] opens an allocation area and sets @hp@ and 
+@hplim@ appropriately. A macro in @include/StgStorage.h@.
+\item[@CloseNursery(hp)@] closes the current allocation area beginning at @hp@.
+A macro in @include/StgStorage.h@.
+\item[@ExtendNursery(hp, hplim)@] closes the current allocation area and 
+tries to find a new allocation area in the nursery. 
+If it succeeds, it sets @hp@ and @hplim@ appropriately and returns @rtsTrue@; 
+otherwise, it returns @rtsFalse@,
+which means that the nursery has been exhausted. 
+The new allocation area is not necessarily contiguous with the old one.
+A macro in @Storage.h@.
+\item[@StgPtr allocate(nat n)@] allocates @n@ words from either the small
+object pool or the large object pool, depending on the argument @n@, 
+and returns a pointer to the first byte. It always succeeds.
+@Storage.c@.
+\end{description}
 
-A {\em generation} is a set of \step{}s 
-(not necessarily contiguous).  Most allocation takes 
-place in generation zero, the youngest generation.
+\subsection{Implementation}
 
-The big difference between generations and \step{}s is that:
-\begin{itemize}
-\item Garbage collection applies to all of generation G and all younger
-  generations (for some G).  You cannot do garbage collection on just
-  part of a generation.
+The nursery is implemented with a fixed number of blocks (@nursery_blocks@
+in @Storage.c@ specifies the number of blocks). 
+Each of these blocks forms its own block group, and they are all linked together
+by @allocNurseries()@ (Figure~\ref{fig-nursery}).
+The head of the linked list is stored in @MainRegTable.rNursery@.
+These blocks are never freed. 
 
-\item Consequently we have to track inter-generational pointers from
-older to younger generations; we do not
-  track inter-\step{} pointers within a generation.
-\end{itemize}
+\begin{figure}[ht]
+\begin{center}
+\input{nursery.eepic}
+\caption{Nursery during the mutator time}
+\label{fig-nursery}
+\end{center}
+\end{figure}
+
+A single block called the \emph{active block} serves as the allocation area 
+at any moment.
+When the free space left in the block is not enough for the request from 
+the mutator, the heap allocator sets the @free@ field in the corresponding
+block descriptor to the first free byte in the block and moves the allocation
+area to the next block. 
+The block descriptor of the active block is stored in 
+@MainRegTable.rCurrentNursery@.
+@Hp@ and @HpLim@ are kept valid during the mutator time so that
+the mutator can obtain fresh memory on its own simply by adjusting @Hp@. 
+
+The blocks in the nursery are carefully allocated in a contiguous address
+range so that they fit next to each other in the cache. 
+
+The small object pool is implemented with a linked list of block groups, 
+each of which consists of a single block (Figure~\ref{fig-smallobjectpool}).
+The head of the linked list is stored in @small_alloc_list@ in @Storage.c@.
+
+\begin{figure}[ht]
+\begin{center}
+\input{smallobjectpool.eepic}
+\caption{Small object pool}
+\label{fig-smallobjectpool}
+\end{center}
+\end{figure}
+
+The allocation in the small object pool is done in the same way as in the
+nursery; @alloc_Hp@ and @alloc_HpLim@ (both defined in @Storage.c@) 
+point to the first free byte and the boundary of the small object pool, 
+respectively.
+Thus, if when @allocate()@ is called and the heap allocator decides to 
+allocate fresh memory in the small object pool, it simply increases @alloc_Hp@
+by the size of memory requested. 
+If the allocation cannot be done in the current small object pool, the 
+heap allocator calls @allocBlock()@ to obtain a new block from the block
+allocator, puts it to the head of the linked list, and 
+sets @alloc_Hp@ and @alloc_HpLim@ appropriately.
+
+The large object pool is also implemented with a (doubly) linked list of block
+groups (Figure~\ref{fig-largeobjectpool}). 
+The difference from the small object pool is that each block group stores only 
+a single object: each time the argument to @allocate()@ is
+greater than a threshold value (computed from @LARGE_OBJECT_THRESHOLD@ 
+in @include/Constants.h@), a new block group accommodating the requested size 
+is created to store a single object. 
+The new block group is put to the head of the list. 
+The head of the linked list is available as @g0s0->large_objects@.\footnote{The 
+meaning of @g0s0@ is explained in the next section.}
+
+\begin{figure}[ht]
+\begin{center}
+\input{largeobjectpool.eepic}
+\caption{Large object pool}
+\label{fig-largeobjectpool}
+\end{center}
+\end{figure}
+
+For implementation details, see @Storage.c@ and @include/StgStorage.h@.
+
+\section{Garbage collector}
+
+The garbage collector finds all the objects unreachable from a given set of
+roots and frees the memory allocated to them. By invoking the
+garbage collector regularly, the storage manager prevents the heap from
+growing indefinitely and allows Haskell programs to be executed at a 
+reasonable memory cost.
+
+The garbage collector in the storage manager is based upon the generational 
+garbage collection algorithm.
+The storage manager records the age for every object in the heap.
+An object surviving one garbage collection grows old by one \emph{step},
+and an object surviving a certain number of garbage collections 
+is promoted to the next \emph{generation}.
+That is, a step can be defined as a collection of objects which have survived
+the same number of garbage collections (or a collection objects which are
+born at some point between two particular successive garbage collections),
+and a generation as a group of steps belonging to a certain range of ages.
+Notice that the unit of garbage collections is not step but generation: 
+a garbage collection applies to all the steps in a generation, and we cannot 
+perform a garbage collection just on part of a generation.
+Furthermore, if a particular generation is garbage collected, so are 
+all the younger generations.\footnote{Some 
+authors define a generation as the set of 
+all the objects created between two particular garbage collection and
+an object cannot change its generation (e.g., 1960's, 1970's, and so on).
+In this document, 
+an object can change its generation as it survives garbage collections
+(e.g., teenagers, 20's, and so on).}
+
+Figure~\ref{fig-generation} illustrates how an object grows old.
+Every object is created in step $0$ of generation $0$. 
+As it survives garbage collections, it is moved to the next step of the
+same generation until it is finally promoted to the next generation and 
+moved to step $0$ of the next generation:
+during a garbage collection of generation $g < G$, live objects from 
+step $s < S$ are moved to step $s + 1$, and live objects from
+the last step $S$ are promoted to step $0$ in generation $g + 1$.
+Live objects in step $0$ of generation $G$ stay in the same step;
+the oldest generation maintains only one step because there is no point
+in aging objects in the oldest generation.
+In this way, objects are given a decent chance of dying before being
+promoted to the next generation.
+
+\begin{figure}[ht]
+\begin{center}
+\input{generation.eepic}
+\caption{Evolution of objects through garbage collections}
+\label{fig-generation}
+\end{center}
+\end{figure}
+
+The main reason that we separate steps from generations is to
+reduce the cost of maintaining \emph{backward inter-generational pointers},
+that is, pointers from older generations to younger generations.
+Suppose that a garbage collection applies to all generations $0$
+through $g$. If an object @O@ in one of these generations is pointed to
+by another object in generation $g' > g$, we cannot free the object @O@
+even though generation $g'$ is out of consideration. Consequently
+we have to track backward inter-generational pointers to perform garbage
+collections correctly.
+Since maintaining backward pointers is costly, we
+choose to track backward inter-generational pointers only;
+we do not track backward inter-step pointers.
+
+By grouping all the objects created between two successive garbage collections
+and grouping multiple age groups into one generation, the garbage
+collector makes an efficient use of heap memory.
+
+\subsection{Steps}
+
+A step has the following structure, defined in 
+@include/StgStorage.h@:
 
-\subsection{The \block{} allocator}
+\begin{code}
+typedef struct _step {
+  unsigned int no;
+  bdescr *blocks;
+  unsigned int n_blocks;
+  bdescr *large_objects;
+  /* additional fields */
+} step;
+\end{code}
 
-The {\em \block{} allocator} is responsible for
-allocating blocks.  It mediates between the higher levels of the
-storage manager and the operating system.
+The fields of a step have the following purposes (Figure~\ref{fig-step}):
 
-It supports the following API:
 \begin{description}
-\item{@InitialiseBlockAllocator()@} initialises the \block{} allocator.
-\item{@bdescr *AllocBlocks( int n )@} allocates a {\em contiguous}
-group of @n@ \block{}s, returning a pointer to the \block{} 
-descriptor of the first.  Returns @NULL@ if the request can't be
-satisfied.  The block descriptors of the group are initialised by the block allocator.
-\item{@FreeBlocks( bdescr *p )@} returns a block group to the block allocator.
-\item{@BDescr( void *p )@} takes a pointer @p@ to any
-byte within a block{} and returns a pointer to its \block{}
-descriptor.  It does no error checking, and fails horribly if the @p@
-does not point into a \block{} allocated by the \block{} allocator.
-It is probably implemented as an @inline@ procedure.
+\item[@no@] indicates the age within its generation. 
+$0$ indicates the youngest step in a generation.
+\item[@blocks@] is a linked list of all the blocks in this step 
+which contain small objects.
+Each block forms its own block group.
+\item[@n\_blocks@] is the number of blocks in the linked list @blocks@.
+\item[@large\_objects@] is a (doubly) linked list of all the block groups 
+in this step which contain large objects. 
+Each block group stores only a single object.
 \end{description}
 
+\begin{figure}[ht]
+\begin{center}
+\input{step.eepic}
+\caption{Memory layout of a step}
+\label{fig-step}
+\end{center}
+\end{figure}
+
+The linked list @blocks@ of step $s$ in generation $g$ is created 
+during a garbage collection
+from live small objects of step $s - 1$ in the same generation 
+(or the last step in the previous generation if $s = 0$).
+The @free@ field in every block descriptor never changes because
+no objects are added after the garbage collection; new objects are created 
+only in step $0$ in generation $0$.
+Likewise, the linked list @large_objects@ is created during a
+garbage collection from live large objects of the previous step. 
+
+There are three exceptions to the above rules. 
+First, both @blocks@ and @large_objects@ of
+step $0$ in generation $0$ are not filled with new objects during a garbage 
+collection. 
+They are simply re-initialized by the garbage collector and 
+grow during during the execution of a program as new objects are
+created.
+Step $0$ in generation $0$ is accessible via a global variable @g0s0@, 
+and this is the reason why the large object pool (described in the previous 
+section) is indeed stored in @g0s0->large_objects@. 
+For the same reason, @MainRegTable.rNursery@ holds the same address as 
+@g0s0->blocks@ all the time. 
+Second, @blocks@ of step $1$ in generation $0$ is created not only from
+the nursery (@blocks@ of step $0$ in the same generation) but also from the 
+small object pool. In other words, all the live small objects created since
+the previous garbage collection, either directly by the mutator or indirectly
+through @allocate()@, are gathered together in the same linked list.
+Finally, step $0$ of the last generation serves the source for itself during
+any garbage collection (because there exists no older step).
 
-\subsection{Implementation}
+\subsection{Generations}
+
+A generation has the following structure, defined in 
+@include/StgStorage.h@:
 
-Our current implementation plan for the \block{} allocator is as
-follows.  It allocates memory from the OS in {\em megablocks}.  A
-megablock is a contiguous chunk of memory of $2^M$ bytes or less,
-starting on a $2^M$ byte boundary.  All the \block{} descriptors are
-laid out in an array at the start of the megablock.  Each is less than 32 bytes
-long.  So to get from an address @p@ to its block descriptor, @BDESCR@
-does this:
 \begin{code}
-  bdescr *BDescr( void *p ) {
-       return (((p>>(K-5)) & Bmask) | (p & Mmask)));
-  }
+typedef struct _generation {
+  unsigned int no;
+  step *steps;
+  unsigned int n_steps;
+  unsigned int max_blocks;
+  StgMutClosure *mut_list;
+  StgMutClosure *mut_once_list;
+  /* additional fields */
+} generation;
 \end{code}
-Depending on how many \block{}s fit in the megablock, the first
-one or more descriptors will be unused.
-
-(An alternative design has the \block{} descriptors at the start of
-each \block{}.  This makes @BDescr@ shorter, but is pessimal for
-cache locality when fiddling with \block{} descriptors.  
-It also means that only the first block in a contiguous chunk of
-blocks can have a descriptor.  That in turn makes life difficult
-for a mostly-copying conservative collector.  Given a possible pointer
-from a conservative region, a MCC collector needs
-to find the start of the object into which the putative pointer
-points.  One way to do this is to scan from the start of the
-contiguous chunk of \block{}s into which the pointer points; and to 
-find that it helps to have a descriptor for each \block{}.)
-
-The \block{} allocator maintains a pool of free \block{}s.
-Because it may be asked for a contiguous chunk of \block{}s it 
-needs to keep the free-list ordered (or something similar).
 
+The fields of a generation have the following purposes (Figure~\ref{fig-gen}):
 
-\section{The Storage Manager API}
-
-The storage manager supports the following API.
 \begin{description}
-\item[@void *Allocate( int n )@] allocates @n@ bytes and returns
-a pointer to the first byte of the block.  Returns @NULL@ if
-the request could not be satisfied --- @Allocate@ does not call
-the garbage collector itself.  It is the responsibility of the caller
-to completely fill in the allocated chunk with objects packed end to
-end. @OpenNursery( void **p_hp, void **p_hplim )@ 
-
-\item[@OpenNursery( void **p\_hp, void **p\_hplim )@]
-returns pointers in @*p_hp@ and @*p_hplim@, to the beginning and end
-of a free chunk of store.  
-More precisely, @*p_hp@ points to the first byte of the region
-and @*p_hplim@ to the first byte beyond the end of the region.
-@*p_hp@ is set to @NULL@ if there is no chunk available.
-
-@OpenNursery@ is used by the main mutuator to get a chunk of
-contiguous store to allocate in.  It allocates until @hp@ gets
-too close to @hplim@ and then calls @ExtendNursery@ to try
-to get a new region of nursery.
-
-\item[@ExtendNursery( void **p\_hp, void **p\_hplim )@]
-returns to the storage manager the region handed out by
-the previous @OpenNursery@ or @ExtendNursery@.  Suppose that 
-this previous call returned with @*p_hp@ set 
-to $hp$ and @p_hplim@ set to
-$hplim$.
-Then the @*p_hplim@ given to @ExtendNursery@ should be the same 
-as $hplim$, and @*p_hp@ should be greater than or equal to $hp$ and 
-less than or equal to $hplim$.  
-
-@ExtendNursery@ tries to find another suitable region.  If it 
-succeeds, it sets @*hp@ and @*hplim@ appropriately; if not, it sets
-@*hp@ to @NULL@.  The new region is not necessarily contiguous 
-with the old one.
-
-\note{Maybe @ExtendNursery@ should be a macro, so that we can pass a
-globally allocated register to it?  (We can't take the address of a register.)
-If so then presumably so should @OpenNursery@.}
-
-\item[@ZapNursery()@] arranges that the next call to @ExtendNursery@
-will fail.  @ZapNursery@ is called by asynchronous interrupts to arrange that 
-execution is brought to an orderly halt.
-
-\item[@GarbageCollect( void *Roots )@] performs garbage collection.
-The parameter @Roots@ is a procedure which is called by the garbage
-collector when it wishes to find all the roots.  This procedure
-should in turn call @MarkRoot@ on each such root.
-
-\item[@void *MarkRoot( void *p )@] informs the garbage collector that
-@p@ is a root.  It returns the new location of the object.
-
-\item[@RecordMutable( void *p )@] informs the storage manager that
-@p@ points to an object that may contain pointers to objects in
-a younger generation.
-
-It is not necessary to call @RecordMutable@ on objects known to
-be in the nursery.
-
-It is only necessary to call @RecordMutable@ once.  For objects that
-are genuinely mutable (like mutable arrays), the object is permanently
-recorded as mutable.  On the other hand, thunks that are updated only
-once can be dropped from the mutables pool once their pointer has been
-dealt with (promoted).
-
-\item{@boolean InNursery( void *p )@} tells whether @p@ points into
-the nursery.  This should be a rather fast test; it is used to guard
-calls to @RecordMutable@.
+\item[@no@] is the generation number.
+\item[@steps@] points to an array of @step@ structures. @steps[@$i$@]@ 
+corresponds to step $i$ in this generation, i.e., 
+@steps[@$i$@].no@ is equal to $i$.
+\item[@n\_steps@] is the number of @step@ structures in the array pointed to 
+by @steps@.
+\item[@max\_blocks@] is the maximum number of blocks allowed in step $0$ of
+this generation. If the number of blocks allocated 
+in step @0@ exceeds @max_blocks@,
+this generation is garbage collected during the next garbage collection.
+\item[@mut\_list@] links all mutable objects in this generation, that is,
+objects whose contents can be updated and hence may contain pointers to
+younger generations. 
+Every object in this linked list is a dynamic object residing in the heap 
+and has a structure compatible with @StgMutClosure@.
+The structure @StgMutClosure@ (@includes/Closures.h@) has a field 
+@mut_link@ (called a mutable link field) of type @StgMutClosure *@, which
+points to the next object in this linked list.
+The end mark of this linked list is a pointer to a statically allocated object 
+@END_MUT_LIST@ (@StoragePriv.h@).
+\item[@mut\_once\_list@] links objects in this generation whose contents 
+cannot be updated any more but may already have pointers to younger generations.
+As with @mut_list@, it links only those objects whose structure is compatible
+with @StgMutClosure@ and ends with @END_MUT_LIST@.
 \end{description}
 
-
-\section{Allocation}
-
-We want to experiment with the idea of keeping the youngest generation
-entirely in the cache, so that an object might be allocated, used,
-and garbage collected without ever hitting main memory.
-
-To do this, generation zero consists of a fixed number of one-\block{}
-\segment{}s, carefully allocated in a contiguous address range so that
-they fit snugly next to each other in the cache.  
-(Large objects are allocated directly into generation 1,
-so that there is no need to find contiguous \block{}s in generation 0 --- Section~\ref{sect:large}.)
-
-After a GC, the storage manager decides how many generation-0 \segment{}s
-(= \block{}s) can be
-allocated before the next minor GC; it chains these \segment{}s together
-through their @SegLink@ fields.  The storage manager then makes the allocation pointer 
-(@Hp@) point to the start of the first \segment{}, and the limit pointer
-(@HpLim@) point to the end of that \segment{}.
-Objects are allocated by incrementing the @Hp@ until
-it hits @HpLim@.  If the @SegLink@ field of the current \segment{} is NIL,
-then it is time for GC; otherwise @Hp@ is set to point to the first data 
-word of the next \segment{},
-and @HpLim@ to the end of the \segment{}, and allocation
-continues.  So between GCs allocation is only interrupted briefly
-(i.e. a handful of instructions)
-to move @Hp@/@HpLim@ on to the next \segment{}.  Actually, this regular
-interruption can be useful, because it makes a good moment to pre-empt
-a thread to schedule another, or to respond to an interrupt.
-(Pre-emption cannot occur at arbitrary times because we are assuming
-an accurate garbage collector.)
-
-We expect there to be two \step{}s in generation zero, \step{} 0 and \step{} 1.
-At a minor GC we promote live objects in \step{} 1, and copy \step{} 0 to
-become a new \step{} 1.  The new \step{} 1 should fit also fit into the cache.
-Suppose there are 16 generation-zero \segment{}s.  A reasonable budget
-might be this:
+\begin{figure}[ht]
 \begin{center}
-\begin{tabular}{l}
-       10 \segment{}s to allocate into (\step{} 0) \\
-       3 \segment{}s for \step{} 1 \\
-       3 \segment{}s free for the new \step{} 1
-\end{tabular}
+\input{gen.eepic}
+\caption{Memory layout of a generation}
+\label{fig-gen}
 \end{center}
+\end{figure}
+
+The garbage collector maintains an array @generations@ of @generation@ structure
+(defined in @Storage.c@), whose size is stored in a runtime system flag 
+(@RtsFlags.GcFlags.generations@). 
+The generation number of each generation coincides with its index into
+the array @generations@, i.e.,  @generations[@$i$@].no@ is equal to $i$.
+
+As mentioned before, lists of objects which may have pointers to younger
+generations are kept per generation, not per step. The youngest generation,
+accessible via a global variable @g0@, does not keep such a list because it
+does not have younger generations.
+
+The oldest generation, accessible via a global variable @oldest_gen@, may
+contain static objects (as opposed to dynamic objects residing in the heap)
+in its list @mut_once_list@. This happens when a static
+thunk, also known as a \emph{constant applicative form} (CAF), is entered.
+When a CAF (corresponding to closure type @THUNK_STATIC@, defined
+in @includes/ClosureTypes.h@) is entered, 
+it is first put in the list @mut_once_list@ of the oldest generation
+and then overwritten with an appropriate static indirection object 
+(corresponding to closure type @IND_STATIC@).\footnote{Actually a static 
+indirection object does not have a @mut\_link@ field.
+We use its @static\_link@ field as a substitute for @mut\_link@.
+See the structure @StgIndStatic@ in @include/Closures.h@.}\footnote{For
+details of this operation, see the macro @UPD\_CAF()@ in @includes/Updates.h@}
+If the CAF is dynamically loaded (e.g., in an interactive environment), it is 
+instead put in a separate linked list @caf_list@ 
+(declared in @Storage.c@). 
+
+The evaluation result of the 
+CAF is stored in a separate dynamic object in the heap and the static 
+indirection object has a pointer to the dynamic object.
+Thus, the new static indirection object is put in the list 
+@mut_once_list@ of the oldest generation (or the list @caf_list@) so that the 
+dynamic object is not removed during the next garbage collection.
+Once it is created, the static indirection object remains unaltered, which
+is the reason why it is put in the @mut_once_list@ list, not in the 
+@mut_list@ list.
+Since the static indirection object survives any garbage collection (because
+it comes from a static object) and would be eventually moved to the oldest 
+generation,
+we put it in the @mut_once_list@ of the oldest generation as soon
+as it is created.
+
+\subsection{Interface}
 
-This assumes a 30\% survival rate from \step{} 0 to \step{} 1.  We can do in-flight
-measurements to tune this.  And (MOST IMPORTANT) if we get it wrong, and
-get a blip in which 9 \segment{}s survive minor GC, all is not lost... we simply
-have to use some new \block{}s and generation zero gets bigger for a while
-and perhaps less cache resident.  (This contrasts with systems which rely
-on contiguous partitions, where an overflow is catastrophic, and which must
-therefore over-reserve.  We get better address-space usage by not having
-to be so conservative.  Again, this isn't a new observation.)
-
-
-\section{Doing garbage collection}
+\begin{description}
+%\item[@StgClosure *MarkRoot(StgClosure *root)@] informs the garbage collector
+%that @root@ is an object in the root set. It returns the new location of 
+%the object. @GC.c@.
+\item[@void *mark\_root(StgClosure **root)@] informs the garbage collector
+that @*root@ is an object in the root set. It replaces @*root@ by 
+the new location of the object. @GC.c@.
+\item[@void GarbageCollect(void (*get\_roots)(evac\_fn), rtsBool force\_major\_gc)@]
+performs a garbage collection. 
+@get_roots()@ is a function which is called by the garbage collector when
+it wishes to find all the objects in the root set (other than those
+it can find itself).
+Therefore it is incumbent on the caller to find the root set.
+@force_major_gc@ specifies whether a major garbage collection is required
+or not. If a major garbage collection is not required, the garbage collector
+decides an oldest generation $g$ to garbage collect on its own.
+@GC.c@.
+\item[@rtsBool doYouWantToGC(void)@] returns @rtsTrue@ if the garbage
+collector is ready to perform a garbage collection. Specifically, it returns
+@rtsTrue@ if the number of allocated blocks since the last garbage collection
+(@alloc_blocks@ in @Storage.c@) exceeds an approximate limit 
+(@alloc_blocks_lim@ in @Storage.c@).
+@Storage.h@.
+\item[@void recordMutable(StgMutClosure *p)@] informs the garbage collector
+that a previously immutable object @p@ has become mutable.
+The garbage collector then puts the object @p@ in the list @mut_list@ of the 
+generation to which it belongs.\footnote{It is easy to 
+locate the generation to which a dynamic object belongs from its address: 
+we can identify the block in which the object resides from its address,
+and the corresponding block descriptor stores pointers 
+to the step and the generation (@gen@ and @step@ fields in the @bdescr@ 
+structure) to which it belongs.}
+It suffices to call @RecordMutable()@ only once for any object. 
+
+For an object which is genuinely mutable (e.g., mutable arrays), 
+it is permanently recorded as mutable. 
+On the other hand, 
+an object which is temporarily mutable (e.g., frozen arrays),
+can be dropped from the list @mut_list@ once its pointer has been dealt with
+during garbage collections. @Storage.h@.
+\item[@void recordOldToNewPtrs(StgMutClosure *p)@] puts the object @p@ in the
+list @mut_once_list@ of the generation to which it belongs.
+\item[@void newCAF(StgClosure *caf)@] puts the CAF @caf@ either 
+in the list @caf_list@ or
+in the list @mut_once_list@ of the oldest generation,
+depending on whether it is dynamically loaded or not.
+\end{description}
 
-When GC happens, we first decide which generation to collect.  (How?)
-When we collect generation G we collect all younger generations too.
+\subsection{Implementation}
 
-To begin with we assume a copying style garbage collection
-algorithm.  The structure of a full GC is as follows:
+The overall structure of a garbage collection is as follows:
 
 \begin{enumerate}
-\item Initialise
-\item For each root @r@ do @r:=Evacuate(r)@.
-\item Scavenge
-\item Tidy up
+\item[(1)] Initialize.
+\item[(2)] Scavenge lists @mut_once_list@ and @mut_list@ if necessary.
+\item[(3)] Scavenge CAFs.
+\item[(4)] Evacuate roots.
+\item[(5)] Scavenge objects.
+\item[(6)] Tidy up.
 \end{enumerate}
 
-A {\em root} is either a pointer held by the RTS (notably the queue
-of runnable threads), or a pointer from a generation > G that might
-point into a generation <= G.
+\subsubsection{(1) Initialization}
+
+During initialization, the garbage collector first decides which generation
+to garbage collect.
+Specifically, 
+if the argument @force_major_gc@ to @GarbageCollect()@ is @rtsFalse@,
+it decides the greatest generation number $N$ such
+that the number of blocks allocated in step $0$ of generation $N$ exceeds
+@generations[@$N$@].max_blocks@. 
+If the argument @force_major_gc@ to @GarbageCollect()@ is @rtsTrue@,
+$N$ is set to the greatest generation number, namely, 
+$@RtsFlags.GcFlags.generations@ - 1$.
+The garbage collector considers up to generation $N$ for garbage collection.
+A major garbage collection takes place if $N$ is set to 
+$@RtsFlags.GcFlags.generations@ - 1$ during this process.
+
+Then, the garbage collector initialize the \emph{to-space} (as opposed to
+\emph{from-space}) for each step of 
+each generation, which is complete with an \emph{allocation pointer} and
+an \emph{sweep pointer}.
+The to-space of a step is the memory to which any object belonging to the
+step can be copied when it survives a garbage collection.
+For instance, a live object in step $s$ of generation $g$ can first be copied
+to the to-space associated with step $s$, which eventually becomes 
+associated with the next step $s + 1$ (or step $0$ of the next generation)
+during tidying up.
+This operation effectively moves an object to the next step if it survives
+a garbage collection. 
+The allocation pointer points to the next free in the to-space while 
+the sweep pointer points to the next object considered for scavenging.
+
+During major garbage collections,
+the static link field of every static object indicates whether it has
+been visited by the garbage collector or not.
+Therefore, the static link field of every static object must have
+a null value before a major garbage collection starts. 
+The list @mut_once_list@ of the oldest generation may contain static 
+indirection objects, and thus 
+the garbage collector invokes @zero_mutable_list()@ on the list,
+Although this breaks up the list, it does not cause any problem because 
+the list is not employed during major garbage collections.
+
+\subsubsection{\tt evacuate()}
+
+The function @evacuate()@ (defined in @GC.c@), which 
+is called eventually for every live object 
+(including even static objects reachable from roots),
+moves an object to
+a safe place so as not to be garbage collected.
+Before invoking the function @evacuate()@ on an object @o@, the caller specifies
+a \emph{desired generation} for @o@ in a variable @evac_gen@
+(declared in @GC.c@).
+The desired generation is the youngest generation to which the caller wishes 
+@o@ to be evacuated; the garbage collector should evacuate @o@ to a 
+generation no younger than the desired generation.
+
+Depending on @evac_gen@ and the generation $M$ where @o@ currently resides,
+@evacuate()@ behaves itself as follows:
+\begin{itemize}
+\item If @evac_gen@ $\leq M$ and $N < M$, it does nothing because @o@ is already
+      in a generation no younger than @evac_gen@.
+\item If @evac_gen@ $\leq M \leq N$, it evacuates @o@ to the to-space of the 
+step to which @o@ currently belongs. @o@ will be moved to the next step later.
+@recordMutable()@ may be invoked on @o@ depending on its type (e.g., @MVAR@).
+\item If $M <$ @evac_gen@, @o@ is evacuated to the to-space of step $0$
+      of generation @even_gen@, which accomplishes the request.
+      This happens even when $N \leq$ @evac_gen@. Therefore, those generations
+      which are not considered for garbage collection may still be augmented
+      with new objects during garbage collection.
+      @recordMutable()@ may be invoked on @o@ depending on its type.
+\end{itemize}
+If @o@ has already been evacuated, @evacuate()@ either does nothing (when
+@even_gen@ $\leq M$) or reports
+a failure to evacuate @o@ by setting the flag @failed_to_evac@ (declared
+in @GC.c@). 
+
+Evacuating a large object is handled by @evacuate_large()@.  
+Since it is costly to allocate new memory blocks and copy all the contents
+of the object, the garbage collector simply removes the object form
+the list @large_alloc_list@ of its step and links it to another list,
+from which it will be scavenged later.
+
+\subsubsection{Set of roots for garbage collection}
+Part of the set of roots for garbage collection is obtained indirectly by 
+invoking the function
+@get_roots()@, an argument to @GarbageCollect()@: the garbage collector
+invokes @get_roots()@ with @mark_root()@ as an argument, and @get_roots()@
+in turn invokes @mark_root()@ on each of known roots.
+The rest of the set of roots is obtained from the lists @mut_list@ and
+@mut_once_list@ of generation $N + 1$ through the oldest generation:
+any objects in these lists may have pointers to objects in generations
+$0$ to $N$, and thus must be considered as a root.
+If a major garbage collection takes place, no @mut_list@ and @mut_once_list@
+lists are consider for scavenging and step (2) is skipped.
+The entire set of roots is now specified by @get_roots()@ alone.
+
+\subsubsection{(2) Scavenging lists {\tt mut\_once\_list} and {\tt mut\_list}}
+
+Since the roots obtained from the lists @mut_list@ and @mut_once_list@ are
+already in generations $N' > N$, we only have to scavenge them.
+That is, it suffices to invoke @evacuate()@ once on each object 
+which is currently pointed to by an object in these lists. 
+
+When scavenging an object @r@ in the list @mut_once_list@ of generation $M$,
+the desired generation is set to $M$ for each object @o@ pointed
+to by @r@ before invoking @evacuate()@. 
+The rationale is that the contents of @r@ cannot be updated any more,
+and thus @r@ is always survived by @o@; @o@ is live as long as @r@ is.
+Therefore, we wish @r@ to be evacuated to the same generation $M$ as @r@
+currently resides (not to its next step).
+If the evacuation succeeds (indicated by a @rtsFalse@ value of a variable
+@failed_to_evac@, declared in @GC.c@) for every object @o@, @r@ is removed 
+from the list @mut_once_list@ because it does not hold any backward 
+inter-generational pointers.\footnote{It turns out that @r@ can have only
+one such object @o@. The type of @r@ is one of the following:
+@IND\_OLDGEN@, @IND\_OLDGEN\_PERM@, @IND\_STATIC@, and @MUT\_VAR@.}
+
+Scavenging a list @mut_list@ is similar to the case of @mut_once_list@.
+When scavenging an object @r@ in the list @mut_list@ of generation $M$,
+the desired generation is set to $M$ for each object pointed to by @r@
+if @r@ is known to be immutable (e.g., @MUT_ARR_PTRS_FROZEN@, 
+@IND_OLDGEN@)
+or to $0$ if @r@ is still mutable (e.g., @MUT_ARR_PTRS@, @MUT_VAR@).
+The list @mut_once_list@ is also adjusted if it is safe to remove @r@ from
+@mut_list@. 
+
+\subsubsection{(3) Scavenging CAFs}
+
+When a dynamically loaded CAF is entered, it it first put to the list 
+@caf_list@ and then overwritten with a static indirection object.
+The evaluation result of the CAF is stored in a dynamic object in the heap
+and the static indirection object stores a pointer to the dynamic object.
+Although the static indirection object (or the CAF) itself is never freed, 
+it may be removed later from the @caf_list@ when it is reverted to the 
+original CAF, and the dynamic object may not be live afterwards.
+Hence, we treat the dynamic object just as normal dynamic objects and
+set the desired generation to $0$.
+
+\subsubsection{(4) Evacuating roots}
+
+Evacuating roots (other than those in the lists @mut_once_list@ and 
+@mut_list@) is simply done by invoking @get_roots()@ with @mark_root()@
+as an argument. 
+Since these roots are normal dynamic objects, we set the desired generation
+to $0$.
+
+\subsubsection{(5) Scavenging}
+
+The garbage collector scavenges all the objects in the to-space of
+each step (by invoking @evacuate()@ on each object reachable from them) 
+until every sweep pointer has reached its corresponding 
+allocation pointer. 
+It repeatedly examines all the to-spaces because not only sweep pointers
+but also allocation pointers change during scavenging:
+when an object @r@ is scavenged, each object reachable from 
+@r@ is evacuated to a certain to-space, which increases the corresponding
+allocation pointer, and
+the sweep pointer of the to-space which currently contains @r@
+increases as well upon finishing scavenging the object @r@.
+Thus, the garbage collector cannot anticipate in advance how many times 
+it needs to scan through all the to-spaces; it keeps scavenging until
+no objects are left to be scavenged.
+
+\subsubsection{Scavenging static objects}
+
+Since it is possible for dynamic objects to point to static objects,
+the garbage collector may invoke @evacuate()@ on static objects 
+while scavenging dynamic objects in to-spaces.
+This complicates the garbage collector because 
+static objects cannot be evacuated in general yet
+they may have pointers to dynamic objects, which must be evacuated.
+Thus the garbage collector needs to at least scavenge live static objects
+(as opposed to those static objects currently not reachable from roots).
+
+When a minor garbage collection is performed, any invocation of
+@evacuate()@ on static objects is simply ignored. 
+Furthermore, no static object is considered for scavenging
+(except those in the list @mut_once_list@ of the oldest generation during).
+Still all dynamic objects which are marked as live due to static objects
+are safely evacuated.
+The reason is that we can reach all such dynamic objects from 
+indirection static objects stored in the list 
+@mut_once_list@ of the oldest generation, which is scavenged during step (2),
+and the list @caf_list@.
+In other words, in order to evacuate all such dynamic objects, it is 
+sufficient to evacuate all dynamic objects reachable from 
+static indirection objects in 
+the list @mut_once_list@ of the oldest generation and the list @caf_list@.
+However, the garbage collector may unnecessarily scavenge certain static 
+indirection objects which are no longer used.
+They are not scavenged during a major garbage collection, however.
+
+During a major garbage collection,
+if an invocation of @evacuate()@ on a static object @r@ is made,
+the garbage collector first checks whether @r@ needs to be scavenged or not.
+If its SRT (Static Reference Table) is empty and it has no other pointers, 
+no dynamic objects are reachable from @r@ and it is ignored.\footnote{If 
+no dynamic objects are reachable from a static object @r@ (even indirectly
+via multiple static objects),
+@r@ is not stored in \emph{any} SRT table because it would be no use attempting
+to follow any pointers in @r@.}
+Otherwise, it is put in the list @static_objects@.
+At the beginning of each scavenging loop in step (5), 
+the garbage collector invokes @scavenge_static()@ if the list @static_objects@
+is not empty.
+@scavenge_static()@ scavenges the static objects in the list @static_objects@
+by invoking @evacuate()@ on every object reachable from them.
+The desired generation is set to the oldest generation (because any
+dynamic object directly pointed to by a static object lives 
+forever).
+These static objects are then put in another list @scavenged_static_objects@
+and removed from the list @static_objects@.
+For a static indirection object, if the evacuation 
+fails, it is put back to the list @mut_once_list@ of the oldest generation;
+it can be thought of as a CAF just entered.
+
+After a major garbage collection, therefore, the list @scavenged_static_objects@
+links all live static objects except for static indirection objects put back
+to the list @mut_once_list@ of the oldest generation. 
+Dynamically loaded CAFs are found in the list @caf_list@.
+
+\subsubsection{(6) Tidying up}
+
+The garbage collector tidies up the heap by 
+moving the to-space of each step to the next step. 
+It also re-initialize the small object pool (which now does not contain
+any live objects), frees any large objects which have not been scavenged,
+and invokes @resetNurseries()@.
+If a major garbage collection has been performed, it 
+invokes @zero_static_object_list()@ on the list @scavenged_static_objects@ 
+so that all static objects
+(other than those in the list @mut_once_list@ of the oldest generation)
+have a null static link field again.
+
+At this point, both the small allocation pool and the large object pool are
+empty. Upon the exit from @GarbageCollect()@, however, they may not
+be empty any more because the garbage collector invokes @scheduleFinalizer()@
+before exiting, which tries to run pending finalizers on dead weak pointers and 
+may create new objects through @allocate()@.
+The nursery still remains intact.
+
+The heap may contain extra objects which are not reachable from the roots
+used during the garbage collection: 1) weak head pointers; 2) dead
+weak head pointers. Weak head pointers can be tracked from 
+the list @weak_ptr_list@ (declared in @Weak.c@). However, there is no way
+of reaching dead weak pointers; they will be garbage collected during the
+next garbage collection.
+
+For implementation details, see @GC.c@.
+
+\section{Miscellaneous notes}
 
-The @Evacuate@ operation does this:
+\begin{itemize}
+\item To see how to add new fields to Haskell closures, 
+see the document on the implementation of retainer profiling 
+(section `Adding Retainer Set Fields').
 
-\begin{code}
-Evacuate(p) { 
-  /* Ignore pointers into non-collected generations */
-  if generation(p) > G then return(p);
+\item To see how to traverse the graph and visit every live closure,
+see the document on the implementation of retainer profiling
+(section `Graph Traversal').
 
-  /* Copy the object and return its new location */
-  p' := copy the object pointed to by p into to-space(g);
+\item To see how to linearly scan the heap at any random moment during
+program execution, see the document on the implementation of LDVU profiling
+(section `Heap Censuses').
 
-  return(p');
-}
-\end{code}
-The @Scavenge@ operation operation does this:
-\begin{code}
-Scavenge() {
-  While there is an un-scavenged object O in to-space do
-       for each pointer q in O do
-               q := Mark(q)
-               remember that O has been scavenged
-}
-\end{code}
-We say ``copy into to-space'' and ``find an un-scavenged object in to-space'',
-but actually each \step{} of each generation has a separate to-space,
-complete with allocation pointer and sweep pointer. (The sweep pointer
-finds the un-scavenged objects.)  When copying
-into a to-space a check must be made for to-space 
-overflow and a new \segment{} chained on if necessary. 
-This is much as when the 
-mutator is doing allocation.  The difference is that we are allocating
-in multiple \segment{}s in an interleaved fashion, so we can't hold
-the allocation pointers in registers.
-
-Only when all the sweep pointers
-have reached the corresponding allocation pointer is GC done.
-This entails multiple passes through the \step{}s until a pass 
-finds no un-scavenged objects.  [NB: if we treated inter-generational pointers
-between generations <= G as roots, then we could avoid some of these multiple
-passes.  But we'd collect less garbage, and hence copy more
-objects, so it would probably be slower.]  
-
-Notice that there is no issue about partitioning the address space
-among generations, as there might be if each lived in a contiguous address
-space.
-
-\section{Large objects}
-\label{sect:large}
-
-Large objects are allocated directly in generation 1, each alone in its
-own \segment{}.  This means that they never, ever, need to be copied!  Instead,
-we simply change their generation or \step{} number.  We also need to chain the
-object onto a list of large objects to be swept for pointers.  This act also
-serves to mark the object, so we don't chain it on twice, or increment its
-\step{} number twice.
-
-Stack objects probably count as large objects.  This is one reason for not
-wanting \block{}s to be too big.
-
-
-\section{Mark sweep}
-
-So far we have assumed copying collection.  Actually, for 
-older generations we may want to use 
-mark/sweep/compact because it is a bit less greedy on address
-space, and may help paging behaviour.
-
-What I have in mind is a pointer-reversing mark phase, so that we don't have
-to allocate space for a mark stack, although one could argue that a mark
-stack will almost invariably be small, and if it gets big then you can always
-malloc some more space.  Mumble mumble.
-
-\bibliographystyle{plain} 
-\bibliography{bibl,simon}
+\item To see how to linearly scan the from-space during garbage collections,
+see the document on the implementation of LDVU profiling
+(section `Destruction of Closures').
+
+\end{itemize}
 
 \end{document}