[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / includes / SMInfoTables.lh
diff --git a/ghc/includes/SMInfoTables.lh b/ghc/includes/SMInfoTables.lh
new file mode 100644 (file)
index 0000000..5a5c33c
--- /dev/null
@@ -0,0 +1,1763 @@
+%
+% (c) The OBFUSCATION-THROUGH-GRATUITOUS-PREPROCESSOR-ABUSE Project,
+%     Glasgow University, 1990-1994
+%
+%************************************************************************
+%*                                                                     *
+\section[info-table-macros]{Info-table macros}
+%*                                                                     *
+%************************************************************************
+
+We define {\em info tables} here.  First, all the different pieces of
+an info table (entry code, evac code, etc.); then all the different
+kinds of info tables (SPEC, DYN, etc).  NB: some of the parallel-only
+kinds are defined in \tr{Parallel.lh}, not here.
+
+An info-table contains several fields. The first field is
+the label of the closure's {\em standard-entry code}. This is used by
+the reducer to ``evaluate'' the closure. The remaining fields are used
+by the garbage collector and other parts of the runtime
+system. Info-tables are declared using the C macros defined below.
+The details of the contents are determined by the storage manager and
+are not of interest outside it.
+
+Info tables may either be {\em reversed} or not.  Reversed is normal
+and preferred, but it requires ``assembler mangling'' of the C
+compiler output.  (The native-code generator does reversed info-tables
+automagically.) With reversed info tables, (a)~the words are reversed
+[obviously], (b)~the info-table's C label addresses the word {\em just
+after} the info table (where its associated entry code ``happens to be''),
+and (c)~the entry-code word in the info table is omitted (it's
+vestigial).
+
+Info-table reversal is hidden behind the @IREL@ macro.
+
+The following fields are used when defining particular info-tables.
+Some sorts of info-table (e.g. @FETCHME_ITBL@) don't need all these
+fields to be specified.
+
+\begin{description}
+\item[@infolbl@]
+The name used to create labels for the info-table, profiling
+information, etc.
+
+\item[\tr{entry_code}:]
+The function which is called when entering the closure.
+
+\item[\tr{update_code}:]
+The function which is called when updating the closure (constructors only).
+
+\item[\tr{tag}:]
+(So much for the Spineless {\em Tagless} G-Machine...)  Used for
+semi-tagging checks.
+
+\item[\tr{type}:]
+Similar-but-different info to the \tr{tag} stuff; the
+parallel world needs more elaborate info.
+
+\item[\tr{size}:]
+The size of the closure (see \tr{SMClosures.lh} for a precise
+definition of ``size'').  Used by the garbage-collector, not the
+Haskell reducer.
+
+\item[\tr{ptrs}:]
+The number of pointers in the closure.  Used by the garbage-collector,
+not the Haskell reducer.
+
+\item[@localness@]
+Whether the info-table is local to this module or not.
+The field is set to @static@ if the info-table is
+local, and is empty otherwise.
+
+\item[@entry_localness@]
+Whether the @entry_code@ routine is local to this module or not.
+This field can have the following values:
+  \begin{description}
+  \item [@EXTFUN@]
+  The entry code is global.
+  \item [@INTFUN@]
+  The entry code is local.
+  \end{description}
+
+\item[@kind@]
+This identifies the general sort of the closure for profiling purposes.
+It can have the following values (defined in CostCentre.lh):
+
+  \begin{description}
+  \item[@CON_K@]
+  A constructor.
+  \item[@FN_K@]
+  A literal function.
+  \item[@PAP_K@]
+  A partial application.
+  \item[@THK_K@]
+  A thunk, or suspension.
+  \item[@BH_K@]
+  A black hole.
+  \item[@ARR_K@]
+  An array.
+  \item[@MP_K@]
+  A Malloc Pointer.
+  \item[@SPT_K@]
+  The Stable Pointer table.  (There should only be one of these but it
+  represents a form of weak space leak since it can't shrink to meet
+  non-demand so it may be worth watching separately? ADR)
+  \item[@INTERNAL_KIND@]
+  Something internal to the runtime system.
+  \end{description}
+
+\item[@descr@]
+This is a string used to identify the closure for profiling purposes.
+\end{description}
+
+So, for example:
+\begin{pseudocode}
+SPEC_N_ITBL(RBH_Save_0_info,RBH_Save_0_entry,UpdErr,0,INFO_OTHER_TAG,2,0,,IF_,INTERNAL_KIND,"RBH-SAVE","RBH_Save_0");
+\end{pseudocode}
+
+%************************************************************************
+%*                                                                     *
+\subsection[info-table-common-up]{The commoned-up info-table world}
+%*                                                                     *
+%************************************************************************
+
+Since lots of info-tables share the same information (which doesn't
+change at run time) needlessly, we gather this common information
+together into a rep-table.
+
+Conditionally present data (concerning the parallel world, and also
+information for the collectors) are gathered into unique rep-tables,
+which are pointed to from info-tables.  This saves several words for
+each closure we build, at the cost of making garbage collection and
+fetching of data from info-tables a little more hairy.
+
+Size and pointers fields go away altogether, save for @GEN@ closures
+where they are tacked on to the end of info-tables.
+
+%************************************************************************
+%*                                                                     *
+\subsection[info-table-common]{Bits common to all info-tables}
+%*                                                                     *
+%************************************************************************
+
+The entry code for a closure, its type, its ``size'', and the number
+of pointer-words it contains are the same in every info table.  For
+the parallel system, two flush code-entries are also standard.
+
+Multi-slurp protection:
+\begin{code}
+#ifndef SMInfoTables_H
+#define SMInfoTables_H
+\end{code}
+
+\begin{code}
+#ifdef __STG_REV_TBLS__
+
+# define IREL(offset)  (-(offset))
+
+/* NB: the ENT_ macro (StgMacros.lh) must also be changed */
+
+# define ENTRY_CODE(infoptr)     ((F_)(infoptr))
+
+#else /* boring non-reversed info tables */
+
+# define IREL(offset)  (offset)
+
+# define ENTRY_CODE(infoptr)     (((FP_)(infoptr))[IREL(0)])
+
+#endif /* non-fixed size info tables */
+\end{code}
+
+\begin{code}
+#define INFO_TAG(infoptr)      ((I_) ((P_)(infoptr))[IREL(1)])
+#define EVAL_TAG(infoptr)      (INFO_TAG(infoptr) >= 0)
+\end{code}
+
+\begin{code}
+
+#define INFO_INTERNAL          (~0L)   /* Should never see this */
+
+#define INFO_UNUSED            (~0L)
+/* We'd like to see this go away in code pointer fields, with specialized code
+   to print out an appropriate error message instead.
+   WDP 94/11: At least make it an Obviously Weird Value?
+ */
+
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection[info-table-rtbl]{Rep tables in an info table}
+%*                                                                     *
+%************************************************************************
+
+Common information is pointed to by the rep table pointer.  We want to
+use extern declarations almost everywhere except for the single module
+(\tr{Rep.lc}) in which the rep tables are declared locally.
+
+\begin{code}
+#if defined(COMPILING_REP_LC) || defined(COMPILING_GHC)
+# define MAYBE_DECLARE_RTBL(l,s,p)
+#else
+# define MAYBE_DECLARE_RTBL(l,s,p)     EXTDATA_RO(MK_REP_REF(l,s,p));
+#endif
+
+#define INFO_RTBL(infoptr)     (((PP_)(infoptr))[IREL(2)])
+\end{code}
+  
+%************************************************************************
+%*                                                                     *
+\subsection{Maybe-there-maybe-not fields in an info table}
+%*                                                                     *
+%************************************************************************
+
+That's about it for the fixed stuff...entry code, a tag and an RTBL pointer.
+
+\begin{code}
+#define FIXED_INFO_WORDS               3
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsubsection{Profiling-only fields in an info table}
+%*                                                                     *
+%************************************************************************
+
+These macros result in the profiling kind and description string being
+included only if required.
+\begin{code}
+#define PROFILING_INFO_OFFSET  (FIXED_INFO_WORDS)
+
+#if !defined(USE_COST_CENTRES)
+# define PROFILING_INFO_WORDS  0
+# define INCLUDE_PROFILING_INFO(base_name)
+# define INREGS_PROFILING_INFO    
+
+#else
+# define PROFILING_INFO_WORDS  1
+
+# define INCLUDE_PROFILING_INFO(base_name) , (W_)REF_CAT_IDENT(base_name)
+# define INREGS_PROFILING_INFO ,INFO_UNUSED
+
+# define INFO_CAT(infoptr)  (((ClCategory *)(infoptr))[IREL(PROFILING_INFO_OFFSET)])
+
+#endif
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsubsection{Non-standard fields in an info table: where they'll be}
+%*                                                                     *
+%************************************************************************
+
+The @UPDATE_CODE@ field is a pointer to the update code for a constructor.
+I believe that constructors are always of the following types:
+
+\begin{itemize}
+\item @CHARLIKE@
+\item @CONST@
+\item @GEN_N@
+\item @INTLIKE@
+\item @SPEC_N@
+\item @STATIC@
+\end{itemize}
+
+Info tables for these types have non-standard update code fields.  In addition,
+because @GEN@ closures have further non-standard fields (size, ptrs), the
+info tables for @GEN_U@ closures also have a non-standard update code field 
+(which is filled in with @StdErrorCode@).
+
+When we're in the parallel world, we also have to know which registers are
+live when we're returning a constructor in registers, so we have a second 
+word for that as well.
+
+\begin{code}
+
+#define UPDATE_INFO_OFFSET  (PROFILING_INFO_OFFSET+PROFILING_INFO_WORDS)
+
+#ifndef PAR
+# define UPDATE_INFO_WORDS    1
+# define INCLUDE_UPDATE_INFO(upd,live) ,(W_)upd
+#else
+# define UPDATE_INFO_WORDS    2
+# define INCLUDE_UPDATE_INFO(upd,live) ,(W_)upd,(W_)live
+#endif
+
+#define UPDATE_CODE(infoptr)   (((FP_)(infoptr))[IREL(UPDATE_INFO_OFFSET)])
+#define INFO_LIVENESS(infoptr) (((P_)(infoptr))[IREL(UPDATE_INFO_OFFSET+1)])
+\end{code}
+
+@GEN@ closures have the size and number of pointers in the info table
+rather than the rep table.  These non-standard fields follow the update
+code field (which is only required for @GEN_N@ closures, but which we
+include in @GEN_U@ closures just to keep this other stuff at a consistent
+offset).
+
+\begin{code}
+#define GEN_INFO_OFFSET            (UPDATE_INFO_OFFSET+UPDATE_INFO_WORDS)
+#define GEN_INFO_WORDS    2
+#define INCLUDE_GEN_INFO(size,ptrs)    ,(W_)size,(W_)ptrs
+
+#define GEN_INFO_SIZE(infoptr)  ((I_)((P_)(infoptr))[IREL(GEN_INFO_OFFSET)])
+#define GEN_INFO_NoPTRS(infoptr) ((I_)((P_)(infoptr))[IREL(GEN_INFO_OFFSET+1)])
+\end{code}
+
+@CONST@ closures have a pointer to a static version of the closure in their
+info tables.  This non-standard field follows their update code field.
+
+\begin{code}
+#define CONST_INFO_OFFSET          (UPDATE_INFO_OFFSET+UPDATE_INFO_WORDS)
+#define CONST_INFO_WORDS    1
+#define INCLUDE_CONST_INFO(closure)    ,(W_)closure
+
+#define CONST_STATIC_CLOSURE(infoptr)  (((PP_)(infoptr))[IREL(CONST_INFO_OFFSET)])
+\end{code}
+
+@STATIC@ closures are like @GEN@ closures in that they also have the
+size and number of pointers in the info table rather than the rep
+table.  Again, these non-standard fields follow the update code field
+(which I believe is not actually needed for STATIC closures).
+
+\begin{code}
+#define STATIC_INFO_OFFSET         (UPDATE_INFO_OFFSET+UPDATE_INFO_WORDS)
+#define STATIC_INFO_WORDS    2
+#define INCLUDE_STATIC_INFO(size,ptrs) ,(W_)size,(W_)ptrs
+
+#define STATIC_INFO_SIZE(infoptr)   ((I_)((P_)(infoptr))[IREL(STATIC_INFO_OFFSET)])
+#define STATIC_INFO_NoPTRS(infoptr) ((I_)((P_)(infoptr))[IREL(STATIC_INFO_OFFSET+1)])
+\end{code}
+
+In the parallel system, all updatable closures have corresponding
+revertible black holes.  When we are assembly-mangling, we guarantee that
+the revertible black hole code precedes the normal entry code, so that
+the RBH info table resides at a fixed offset from the normal info table.
+Otherwise, we add the RBH info table pointer to the end of the normal
+info table and vice versa.
+
+\begin{code}
+#ifdef PAR
+# define RBH_INFO_OFFSET           (GEN_INFO_OFFSET+GEN_INFO_WORDS)
+
+# define INCLUDE_SPEC_PADDING                          \
+       INCLUDE_UPDATE_INFO(INFO_UNUSED,INFO_UNUSED)    \
+       INCLUDE_GEN_INFO(INFO_UNUSED,INFO_UNUSED)
+
+# ifdef RBH_MAGIC_OFFSET
+
+#  define RBH_INFO_WORDS    0
+#  define INCLUDE_RBH_INFO(infoptr)
+
+#  define RBH_INFOPTR(infoptr)     (((P_)infoptr) - RBH_MAGIC_OFFSET)
+#  define REVERT_INFOPTR(infoptr)   (((P_)infoptr) + RBH_MAGIC_OFFSET)
+
+# else
+
+#  define RBH_INFO_WORDS    1
+#  define INCLUDE_RBH_INFO(infoptr) ,(W_)infoptr
+
+#  define RBH_INFOPTR(infoptr)     (((PP_)(infoptr))[IREL(RBH_INFO_OFFSET)])
+#  define REVERT_INFOPTR(infoptr)   (((PP_)(infoptr))[IREL(RBH_INFO_OFFSET)])
+
+# endif
+
+EXTFUN(RBH_entry);
+P_ convertToRBH PROTO((P_ closure));
+void convertToFetchMe PROTO((P_ closure, globalAddr *ga));
+#endif
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection{Maybe-there-maybe-not fields in a rep table}
+%*                                                                     *
+%************************************************************************
+
+%************************************************************************
+%*                                                                     *
+\subsubsection{Type field in a rep table}
+%*                                                                     *
+%************************************************************************
+
+The @INFO_TYPE@ field in the rep table tells what sort of animal
+the closure is.  
+
+\begin{code}
+#define TYPE_INFO_OFFSET  0
+#define TYPE_INFO_WORDS    1
+#define INCLUDE_TYPE_INFO(kind) (W_)CAT3(INFO_,kind,_TYPE)
+
+#define INFO_TYPE(infoptr)     (((P_)(INFO_RTBL(infoptr)))[TYPE_INFO_OFFSET])
+\end{code}
+
+The least significant 9 bits of the info-type are used as follows:
+
+\begin{tabular}{||l|l||}                                                  \hline
+Bit & Interpretation                                                   \\ \hline
+0   & 1 $\Rightarrow$ Head normal form                                 \\
+1   & 1 $\Rightarrow$ Don't spark me  (Any HNF will have this set to 1)        \\
+2   & 1 $\Rightarrow$ This is a static closure                         \\
+3   & 1 $\Rightarrow$ Has mutable pointer fields \\ 
+4   & 1 $\Rightarrow$ May be updated (inconsistent with being a HNF) \\ 
+5   & 1 $\Rightarrow$ Is a "primitive" array (a BIG structure) \\
+6   & 1 $\Rightarrow$ Is a black hole                                  \\
+7   & 1 $\Rightarrow$ Is an indirection                                        \\
+8   & 1 $\Rightarrow$ Is a thunk                                       \\
+\hline
+\end{tabular}
+
+Updatable structures (@_UP@) are thunks that may be shared.  Primitive
+arrays (@_BM@ -- Big Mothers) are structures that are always held
+in-memory (basically extensions of a closure).  Because there may be
+offsets into these arrays, a primitive array cannot be handled as a
+FetchMe in the parallel system, but must be shipped in its entirety if
+its parent closure is shipped.
+
+\begin{code}
+#define        IP_TAG_BITS             9
+
+#define _NF                    0x0001  /* Normal form  */
+#define _NS                    0x0002  /* Don't spark  */
+#define _ST                    0x0004  /* Is static    */
+#define _MU                    0x0008  /* Is mutable   */
+#define _UP                    0x0010  /* Is updatable (but not mutable) */
+#define _BM                    0x0020  /* Is a "primitive" array */
+#define _BH                    0x0040  /* Is a black hole */
+#define _IN                    0x0080  /* Is an indirection */
+#define _TH                    0x0100  /* Is a thunk */
+
+#define        IS_NF(infoptr)          ((INFO_TYPE(infoptr)&_NF) != 0)
+#define        IS_MUTABLE(infoptr)     ((INFO_TYPE(infoptr)&_MU) != 0)
+#define        IS_STATIC(infoptr)      ((INFO_TYPE(infoptr)&_ST) != 0)
+#define        IS_UPDATABLE(infoptr)   ((INFO_TYPE(infoptr)&_UP) != 0)
+#define        IS_BIG_MOTHER(infoptr)  ((INFO_TYPE(infoptr)&_BM) != 0)
+#define IS_BLACK_HOLE(infoptr) ((INFO_TYPE(infoptr)&_BH) != 0)
+#define IS_INDIRECTION(infoptr)        ((INFO_TYPE(infoptr)&_IN) != 0)
+#define IS_THUNK(infoptr)      ((INFO_TYPE(infoptr)&_TH) != 0)
+
+#define        SHOULD_SPARK(closure)   ((INFO_TYPE(INFO_PTR(closure))&_NS) == 0)
+\end{code}
+
+The other bits in the info-type field simply give a unique bit-pattern
+to identify the closure type.
+
+\begin{code}
+#define        IP_TAG_BIT_MASK         ((1L<<IP_TAG_BITS)-1)
+
+#define BASE_INFO_TYPE(infoptr)        (INFO_TYPE(infoptr) & (~IP_TAG_BIT_MASK)) /* Strips out the tag bits */
+
+#define MAKE_BASE_INFO_TYPE(x) ((x) << IP_TAG_BITS)
+
+#define INFO_SPEC_TYPE         (MAKE_BASE_INFO_TYPE(1L))
+#define INFO_GEN_TYPE          (MAKE_BASE_INFO_TYPE(2L))
+#define INFO_DYN_TYPE          (MAKE_BASE_INFO_TYPE(3L) | _NF | _NS)
+#define INFO_TUPLE_TYPE                (MAKE_BASE_INFO_TYPE(4L) | _NF | _NS | _BM)
+#define INFO_DATA_TYPE         (MAKE_BASE_INFO_TYPE(5L) | _NF | _NS | _BM)
+#define INFO_MUTUPLE_TYPE      (MAKE_BASE_INFO_TYPE(6L) | _NF | _NS | _MU | _BM)
+#define INFO_IMMUTUPLE_TYPE    (MAKE_BASE_INFO_TYPE(7L) | _NF | _NS | _BM)
+#define INFO_STATIC_TYPE       (MAKE_BASE_INFO_TYPE(8L) | _NS | _ST)
+#define INFO_CONST_TYPE                (MAKE_BASE_INFO_TYPE(9L) | _NF | _NS)
+#define INFO_CHARLIKE_TYPE     (MAKE_BASE_INFO_TYPE(10L) | _NF | _NS)
+#define INFO_INTLIKE_TYPE      (MAKE_BASE_INFO_TYPE(11L) | _NF | _NS)
+#define INFO_BH_TYPE           (MAKE_BASE_INFO_TYPE(12L) | _NS | _BH)
+#define INFO_BQ_TYPE           (MAKE_BASE_INFO_TYPE(13L) | _NS | _MU | _BH)
+#define INFO_IND_TYPE          (MAKE_BASE_INFO_TYPE(14L) | _NS | _IN)
+#define INFO_CAF_TYPE          (MAKE_BASE_INFO_TYPE(15L) | _NF | _NS | _ST | _IN)
+#define INFO_FM_TYPE           (MAKE_BASE_INFO_TYPE(16L))
+#define INFO_TSO_TYPE          (MAKE_BASE_INFO_TYPE(17L) | _MU)
+#define INFO_STKO_TYPE         (MAKE_BASE_INFO_TYPE(18L))
+#define INFO_SPEC_RBH_TYPE     (MAKE_BASE_INFO_TYPE(19L) | _NS | _MU | _BH)
+#define INFO_GEN_RBH_TYPE      (MAKE_BASE_INFO_TYPE(20L) | _NS | _MU | _BH)
+#define INFO_BF_TYPE           (MAKE_BASE_INFO_TYPE(21L) | _NS | _MU | _BH)
+#define INFO_INTERNAL_TYPE     (MAKE_BASE_INFO_TYPE(22L))
+
+#define INFO_SPEC_N_TYPE       (INFO_SPEC_TYPE | _NF | _NS)
+#define INFO_SPEC_S_TYPE       (INFO_SPEC_TYPE | _TH)
+#define INFO_SPEC_U_TYPE       (INFO_SPEC_TYPE | _UP | _TH)
+
+#define INFO_GEN_N_TYPE                (INFO_GEN_TYPE | _NF | _NS)
+#define INFO_GEN_S_TYPE                (INFO_GEN_TYPE | _TH)
+#define INFO_GEN_U_TYPE                (INFO_GEN_TYPE | _UP | _TH)
+
+#define INFO_BH_N_TYPE         (INFO_BH_TYPE)
+#define INFO_BH_U_TYPE         (INFO_BH_TYPE | _UP)
+
+#define INFO_STKO_DYNAMIC_TYPE (INFO_STKO_TYPE | _MU)
+#define INFO_STKO_STATIC_TYPE  (INFO_STKO_TYPE | _ST)
+
+#define INFO_FETCHME_TYPE      (INFO_FM_TYPE | _MU)
+#define INFO_FMBQ_TYPE         (INFO_FM_TYPE | _MU | _BH)
+
+#define MIN_INFO_TYPE          0
+#define MAX_INFO_TYPE          INFO_INTERNAL_TYPE
+
+\end{code}
+
+Notes:
+
+An indirection either points to HNF (post update); or is result of
+overwriting a FetchMe, in which case the thing fetched is either
+under evaluation (BH), or by now an HNF.  Thus, indirections get @_NS@.
+
+%************************************************************************
+%*                                                                     *
+\subsubsection{Size/no-of-pointers fields in a rep table}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+#define SIZE_INFO_OFFSET  (TYPE_INFO_OFFSET+TYPE_INFO_WORDS)
+#define SIZE_INFO_WORDS          2
+#define INCLUDE_SIZE_INFO(size,ptrs) ,(W_)size, (W_)ptrs
+
+#define INFO_SIZE(infoptr)   ((I_)((FP_)(INFO_RTBL(infoptr)))[SIZE_INFO_OFFSET])
+#define INFO_NoPTRS(infoptr) ((I_)((FP_)(INFO_RTBL(infoptr)))[SIZE_INFO_OFFSET+1])
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsubsection{Parallel-only fields in a rep table}
+%*                                                                     *
+%************************************************************************
+
+There is now nothing that is specific to the parallel world (GUM), but
+this could change so don't go deleting this little lot!  KH
+
+\begin{code}
+# define PAR_INFO_OFFSET               (SIZE_INFO_OFFSET+SIZE_INFO_WORDS)
+
+/* now the bits that are either on or off: */
+
+# define PAR_INFO_WORDS                0
+# define INCLUDE_PAR_INFO
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsubsection{Copying-only fields in a rep table}
+%*                                                                     *
+%************************************************************************
+
+These macros result in the copying garbage collection code being
+included only if required.
+\begin{code}
+#if defined(_INFO_COPYING)
+# include "SMcopying.h" /* Copying Code Labels */
+# define COPY_INFO_OFFSET  (PAR_INFO_OFFSET+PAR_INFO_WORDS)
+# define COPY_INFO_WORDS 2
+# define INCLUDE_COPYING_INFO(evac, scav) ,(W_)evac,(W_)scav
+
+/* 
+ * use these if you have an unquenchable urge to dig around in
+ *  info tables (e.g., runtime/.../StgDebug.lc)
+ */
+
+# define INFO_EVAC_2S(infoptr)  (((FP_)(INFO_RTBL(infoptr)))[COPY_INFO_OFFSET])
+# define INFO_SCAV_2S(infoptr)  (((FP_)(INFO_RTBL(infoptr)))[COPY_INFO_OFFSET + 1])
+
+# if defined(UPDATES_ENTERED_COUNT)
+
+/* Don't commmon up CONST CHARLIKE and INTLIKE treat as SPEC 1_0 closure */
+/* This broke it -- turning it off. Use LARGE heap so no GC needed */
+#  if 0
+#   define INCLUDE_COPYING_INFO_CONST(evac, scav) \
+       INCLUDE_COPYING_INFO(_Evacuate_1,_Scavenge_1_0)
+#  endif /* 0 */
+
+#  define INCLUDE_COPYING_INFO_CONST(evac, scav) \
+       INCLUDE_COPYING_INFO(evac, scav)
+# else
+#  define INCLUDE_COPYING_INFO_CONST(evac, scav) \
+       INCLUDE_COPYING_INFO(evac, scav)
+# endif
+
+#else  /* ! _INFO_COPYING */
+
+# define COPY_INFO_WORDS 0
+# define INCLUDE_COPYING_INFO(evac, scav)
+# define INCLUDE_COPYING_INFO_CONST(evac, scav)
+
+#endif /* ! _INFO_COPYING */
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsubsection{Compacting-only fields in a rep table}
+%*                                                                     *
+%************************************************************************
+
+These macros result in the compacting garbage collection code being
+included only if required. This includes the variable length
+specialised marking code.
+
+\begin{code}
+#if !defined(_INFO_COMPACTING)
+
+# define INCLUDE_COMPACTING_INFO(scanlink,prmark,scanmove,marking)
+# define SPEC_COMPACTING_INFO(scanlink,prmark,scanmove,marking)
+
+#else /* defined(_INFO_COMPACTING) */
+
+# include "SMcompact.h"        /* Single Space Compacting Code */
+# include "SMmark.h"           /* Pointer Reversal Marking Code Labels */
+
+/* For SPEC closures compacting info is variable length -> must come last */
+
+# define COMPACTING_INFO_OFFSET  (COPY_INFO_OFFSET+COPY_INFO_WORDS)
+
+# define INCLUDE_COMPACTING_INFO(scanlink,prmark,scanmove,marking) \
+       ,(W_)scanlink,(W_)prmark \
+       ,(W_)scanmove,(W_)marking
+
+# define SPEC_COMPACTING_INFO(scanlink,prmark,scanmove,prreturn) \
+       ,(W_)scanlink,(W_)prmark \
+       ,(W_)scanmove, \
+        (W_)prreturn
+
+
+# define INFO_SCAN_LINK_1S(infoptr)    (((FP_)(INFO_RTBL(infoptr)))[COMPACTING_INFO_OFFSET])
+# define INFO_MARK_1S(infoptr)         (((FP_)(INFO_RTBL(infoptr)))[COMPACTING_INFO_OFFSET+1])
+# define INFO_SCAN_MOVE_1S(infoptr)    (((FP_)(INFO_RTBL(infoptr)))[COMPACTING_INFO_OFFSET+2])
+# define INFO_MARKED_1S(infoptr)       (((FP_)(INFO_RTBL(infoptr)))[COMPACTING_INFO_OFFSET+3])
+# define INFO_MARKING_1S(infoptr)      (((FP_)(INFO_RTBL(infoptr)))[COMPACTING_INFO_OFFSET+4])
+
+#ifndef COMPILING_GHC
+extern F_ _Dummy_Static_entry(STG_NO_ARGS);
+extern F_ _Dummy_Ind_entry(STG_NO_ARGS);
+extern F_ _Dummy_Caf_entry(STG_NO_ARGS);
+extern F_ _Dummy_Const_entry(STG_NO_ARGS);
+extern F_ _Dummy_CharLike_entry(STG_NO_ARGS);
+#endif
+
+#endif /* _INFO_COMPACTING */
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[SPEC_ITBL]{@SPEC_x_ITBL@: @SPEC@ info-tables}
+%*                                                                     *
+%************************************************************************
+
+Normal-form and updatable (non-normal-form) variants.
+
+\begin{code}
+
+#define SPEC_N_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Spec_N,size,ptrs)      \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+       }
+
+MAYBE_DECLARE_RTBL(Spec_N,1,0)
+MAYBE_DECLARE_RTBL(Spec_N,1,1)
+MAYBE_DECLARE_RTBL(Spec_N,2,0)
+MAYBE_DECLARE_RTBL(Spec_N,2,1)
+MAYBE_DECLARE_RTBL(Spec_N,2,2)
+MAYBE_DECLARE_RTBL(Spec_N,3,0)
+MAYBE_DECLARE_RTBL(Spec_N,3,1)
+MAYBE_DECLARE_RTBL(Spec_N,3,2)
+MAYBE_DECLARE_RTBL(Spec_N,3,3)
+MAYBE_DECLARE_RTBL(Spec_N,4,0)
+MAYBE_DECLARE_RTBL(Spec_N,4,4)
+MAYBE_DECLARE_RTBL(Spec_N,5,0)
+MAYBE_DECLARE_RTBL(Spec_N,5,5)
+MAYBE_DECLARE_RTBL(Spec_N,6,6)
+MAYBE_DECLARE_RTBL(Spec_N,7,7)
+MAYBE_DECLARE_RTBL(Spec_N,8,8)
+MAYBE_DECLARE_RTBL(Spec_N,9,9)
+MAYBE_DECLARE_RTBL(Spec_N,10,10)
+MAYBE_DECLARE_RTBL(Spec_N,11,11)
+MAYBE_DECLARE_RTBL(Spec_N,12,12)
+
+#define SPEC_N_RTBL(size,ptrs)                                                         \
+    const W_ MK_REP_LBL(Spec_N,size,ptrs)[] = {                                        \
+       INCLUDE_TYPE_INFO(SPEC_N)                                               \
+       INCLUDE_SIZE_INFO(size,ptrs)                                            \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(CAT2(_Evacuate_,size),CAT4(_Scavenge_,size,_,ptrs)) \
+       SPEC_COMPACTING_INFO(CAT4(_ScanLink_,size,_,ptrs),                      \
+                            CAT2(_PRStart_,ptrs),                              \
+                            CAT2(_ScanMove_,size),CAT2(_PRIn_,ptrs))           \
+       }
+
+#define SPEC_S_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Spec_S,size,ptrs)      \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+       }
+
+MAYBE_DECLARE_RTBL(Spec_S,1,0)
+MAYBE_DECLARE_RTBL(Spec_S,1,1)
+MAYBE_DECLARE_RTBL(Spec_S,2,0)
+MAYBE_DECLARE_RTBL(Spec_S,2,1)
+MAYBE_DECLARE_RTBL(Spec_S,2,2)
+MAYBE_DECLARE_RTBL(Spec_S,3,0)
+MAYBE_DECLARE_RTBL(Spec_S,3,1)
+MAYBE_DECLARE_RTBL(Spec_S,3,2)
+MAYBE_DECLARE_RTBL(Spec_S,3,3)
+MAYBE_DECLARE_RTBL(Spec_S,4,0)
+MAYBE_DECLARE_RTBL(Spec_S,4,4)
+MAYBE_DECLARE_RTBL(Spec_S,5,0)
+MAYBE_DECLARE_RTBL(Spec_S,5,5)
+MAYBE_DECLARE_RTBL(Spec_S,6,6)
+MAYBE_DECLARE_RTBL(Spec_S,7,7)
+MAYBE_DECLARE_RTBL(Spec_S,8,8)
+MAYBE_DECLARE_RTBL(Spec_S,9,9)
+MAYBE_DECLARE_RTBL(Spec_S,10,10)
+MAYBE_DECLARE_RTBL(Spec_S,11,11)
+MAYBE_DECLARE_RTBL(Spec_S,12,12)
+
+#define SPEC_S_RTBL(size,ptrs)                                                         \
+    const W_ MK_REP_LBL(Spec_S,size,ptrs)[] = {                                        \
+       INCLUDE_TYPE_INFO(SPEC_S)                                               \
+       INCLUDE_SIZE_INFO(size,ptrs)                                            \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(CAT2(_Evacuate_,size),CAT4(_Scavenge_,size,_,ptrs)) \
+       SPEC_COMPACTING_INFO(CAT4(_ScanLink_,size,_,ptrs),                      \
+                            CAT2(_PRStart_,ptrs),                              \
+                            CAT2(_ScanMove_,size),CAT2(_PRIn_,ptrs))           \
+       }
+
+#ifdef PAR
+# define SPEC_U_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    entry_localness(CAT2(RBH_,entry_code));            \
+    localness W_ infolbl[];                    \
+     localness W_ CAT2(RBH_,infolbl)[] = {     \
+        (W_) CAT2(RBH_,entry_code)             \
+       ,(W_) INFO_OTHER_TAG                    \
+       ,(W_) MK_REP_REF(Spec_RBH,size,ptrs)    \
+       INCLUDE_PROFILING_INFO(RBH)             \
+       INCLUDE_SPEC_PADDING                    \
+       INCLUDE_RBH_INFO(infolbl)               \
+       };                                      \
+    STGFUN(CAT2(RBH_,entry_code)) { JMP_(RBH_entry); }\
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Spec_U,size,ptrs)      \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_SPEC_PADDING                    \
+       INCLUDE_RBH_INFO(CAT2(RBH_,infolbl))    \
+       }
+
+MAYBE_DECLARE_RTBL(Spec_RBH,1,0)
+MAYBE_DECLARE_RTBL(Spec_RBH,1,1)
+MAYBE_DECLARE_RTBL(Spec_RBH,2,0)
+MAYBE_DECLARE_RTBL(Spec_RBH,2,1)
+MAYBE_DECLARE_RTBL(Spec_RBH,2,2)
+MAYBE_DECLARE_RTBL(Spec_RBH,3,0)
+MAYBE_DECLARE_RTBL(Spec_RBH,3,1)
+MAYBE_DECLARE_RTBL(Spec_RBH,3,2)
+MAYBE_DECLARE_RTBL(Spec_RBH,3,3)
+MAYBE_DECLARE_RTBL(Spec_RBH,4,0)
+MAYBE_DECLARE_RTBL(Spec_RBH,4,4)
+MAYBE_DECLARE_RTBL(Spec_RBH,5,0)
+MAYBE_DECLARE_RTBL(Spec_RBH,5,5)
+MAYBE_DECLARE_RTBL(Spec_RBH,6,6)
+MAYBE_DECLARE_RTBL(Spec_RBH,7,7)
+MAYBE_DECLARE_RTBL(Spec_RBH,8,8)
+MAYBE_DECLARE_RTBL(Spec_RBH,9,9)
+MAYBE_DECLARE_RTBL(Spec_RBH,10,10)
+MAYBE_DECLARE_RTBL(Spec_RBH,11,11)
+MAYBE_DECLARE_RTBL(Spec_RBH,12,12)
+
+#define SPEC_RBH_RTBL(size,ptrs)                                               \
+    const W_ MK_REP_LBL(Spec_RBH,size,ptrs)[] = {                              \
+       INCLUDE_TYPE_INFO(SPEC_RBH)                                             \
+       INCLUDE_SIZE_INFO(size,ptrs)                                            \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(CAT2(_Evacuate_RBH_,size),CAT4(_Scavenge_RBH_,size,_,ptrs)) \
+       SPEC_COMPACTING_INFO(CAT4(_ScanLink_RBH_,size,_,ptrs),                  \
+                            CAT2(_PRStart_RBH_,ptrs),                          \
+                            CAT2(_ScanMove_RBH_,size),CAT2(_PRIn_RBH_,ptrs))   \
+       }
+
+#define _Scavenge_RBH_2_0   _Scavenge_RBH_2_1
+#define _Scavenge_RBH_2_2   _Scavenge_RBH_2_1
+
+#define _Scavenge_RBH_3_0   _Scavenge_RBH_3_1
+#define _Scavenge_RBH_3_2   _Scavenge_RBH_3_1
+
+#define _Scavenge_RBH_4_0   _Scavenge_RBH_4_1
+#define _Scavenge_RBH_5_0   _Scavenge_RBH_5_1
+#define _Scavenge_RBH_6_0   _Scavenge_RBH_6_1
+#define _Scavenge_RBH_7_0   _Scavenge_RBH_7_1
+#define _Scavenge_RBH_8_0   _Scavenge_RBH_8_1
+#define _Scavenge_RBH_9_0   _Scavenge_RBH_9_1
+#define _Scavenge_RBH_10_0   _Scavenge_RBH_10_1
+#define _Scavenge_RBH_11_0   _Scavenge_RBH_11_1
+#define _Scavenge_RBH_12_0   _Scavenge_RBH_12_1
+
+#define _ScanLink_RBH_2_0   _ScanLink_RBH_2_1
+#define _ScanLink_RBH_2_2   _ScanLink_RBH_2_1
+
+#define _ScanLink_RBH_3_0   _ScanLink_RBH_3_1
+#define _ScanLink_RBH_3_2   _ScanLink_RBH_3_1
+
+#define _ScanLink_RBH_4_0   _ScanLink_RBH_4_1
+#define _ScanLink_RBH_5_0   _ScanLink_RBH_5_1
+#define _ScanLink_RBH_6_0   _ScanLink_RBH_6_1
+#define _ScanLink_RBH_7_0   _ScanLink_RBH_7_1
+#define _ScanLink_RBH_8_0   _ScanLink_RBH_8_1
+#define _ScanLink_RBH_9_0   _ScanLink_RBH_9_1
+#define _ScanLink_RBH_10_0   _ScanLink_RBH_10_1
+#define _ScanLink_RBH_11_0   _ScanLink_RBH_11_1
+#define _ScanLink_RBH_12_0   _ScanLink_RBH_12_1
+
+#define _PRStart_RBH_0 _PRStart_RBH_2
+#define _PRStart_RBH_1 _PRStart_RBH_2
+
+#define _PRIn_RBH_0    _PRIn_RBH_2
+#define _PRIn_RBH_1    _PRIn_RBH_2
+
+#else
+
+# define SPEC_U_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Spec_U,size,ptrs)      \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       }
+#endif
+
+MAYBE_DECLARE_RTBL(Spec_U,1,0)
+MAYBE_DECLARE_RTBL(Spec_U,1,1)
+MAYBE_DECLARE_RTBL(Spec_U,2,0)
+MAYBE_DECLARE_RTBL(Spec_U,2,1)
+MAYBE_DECLARE_RTBL(Spec_U,2,2)
+MAYBE_DECLARE_RTBL(Spec_U,3,0)
+MAYBE_DECLARE_RTBL(Spec_U,3,1)
+MAYBE_DECLARE_RTBL(Spec_U,3,2)
+MAYBE_DECLARE_RTBL(Spec_U,3,3)
+MAYBE_DECLARE_RTBL(Spec_U,4,0)
+MAYBE_DECLARE_RTBL(Spec_U,4,4)
+MAYBE_DECLARE_RTBL(Spec_U,5,0)
+MAYBE_DECLARE_RTBL(Spec_U,5,5)
+MAYBE_DECLARE_RTBL(Spec_U,6,6)
+MAYBE_DECLARE_RTBL(Spec_U,7,7)
+MAYBE_DECLARE_RTBL(Spec_U,8,8)
+MAYBE_DECLARE_RTBL(Spec_U,9,9)
+MAYBE_DECLARE_RTBL(Spec_U,10,10)
+MAYBE_DECLARE_RTBL(Spec_U,11,11)
+MAYBE_DECLARE_RTBL(Spec_U,12,12)
+
+#define SPEC_U_RTBL(size,ptrs)                                                 \
+    const W_ MK_REP_LBL(Spec_U,size,ptrs)[] = {                                        \
+       INCLUDE_TYPE_INFO(SPEC_U)                                               \
+       INCLUDE_SIZE_INFO(size,ptrs)                                            \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(CAT2(_Evacuate_,size),CAT4(_Scavenge_,size,_,ptrs)) \
+       SPEC_COMPACTING_INFO(CAT4(_ScanLink_,size,_,ptrs),                      \
+                            CAT2(_PRStart_,ptrs),                              \
+                            CAT2(_ScanMove_,size),CAT2(_PRIn_,ptrs))           \
+       }
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[SELECT_ITBL]{@SELECT_ITBL@: Special @SPEC_U@ info-table for selectors}
+%*                                                                     *
+%************************************************************************
+
+These are different only in having slightly-magic GC code.  The idea
+is: it is a @MIN_UPD_SIZE@ (==2) thunk with one pointer, which, when
+entered, will select word $i$ from its pointee.
+
+When garbage-collecting such a closure, we ``peek'' at the pointee's
+tag (in its info table).  If it is evaluated, then we go ahead and do
+the selection---which is {\em just like an indirection}.  If it is not
+evaluated, we carry on {\em exactly as if it is a size-2/1-ptr thunk}.
+
+Copying: only the evacuate routine needs to be special.
+
+Compacting: only the PRStart (marking) routine needs to be special.
+
+\begin{code}
+
+#ifdef PAR
+# define SELECT_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,select_word_i,kind,descr,type) \
+    entry_localness(CAT2(RBH_,entry_code));            \
+    localness W_ infolbl[];                    \
+    localness W_ CAT2(RBH_,infolbl)[] = {      \
+        (W_) CAT2(RBH_,entry_code)             \
+       ,(W_) INFO_OTHER_TAG                    \
+       ,(W_) MK_REP_REF(Spec_RBH,size,ptrs)    \
+       INCLUDE_PROFILING_INFO(RBH)             \
+       INCLUDE_SPEC_PADDING                    \
+       INCLUDE_RBH_INFO(infolbl)               \
+       };                                      \
+    STGFUN(CAT2(RBH_,entry_code)) { JMP_(RBH_entry); }\
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Select,,select_word_i) \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_SPEC_PADDING                    \
+       INCLUDE_RBH_INFO(CAT2(RBH_,infolbl))    \
+       }                                       \
+
+#else
+
+# define SELECT_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,select_word_i,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Select,,select_word_i) \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       }
+
+#endif
+
+MAYBE_DECLARE_RTBL(Select,,0)
+MAYBE_DECLARE_RTBL(Select,,1)
+MAYBE_DECLARE_RTBL(Select,,2)
+MAYBE_DECLARE_RTBL(Select,,3)
+MAYBE_DECLARE_RTBL(Select,,4)
+MAYBE_DECLARE_RTBL(Select,,5)
+MAYBE_DECLARE_RTBL(Select,,6)
+MAYBE_DECLARE_RTBL(Select,,7)
+MAYBE_DECLARE_RTBL(Select,,8)
+MAYBE_DECLARE_RTBL(Select,,9)
+MAYBE_DECLARE_RTBL(Select,,10)
+MAYBE_DECLARE_RTBL(Select,,11)
+MAYBE_DECLARE_RTBL(Select,,12)
+
+#define SELECT_RTBL(size,ptrs,select_word_i)                                   \
+    const W_ MK_REP_LBL(Select,,select_word_i)[] = {                           \
+       INCLUDE_TYPE_INFO(SPEC_U)                                               \
+       INCLUDE_SIZE_INFO(size,ptrs)                                            \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(CAT2(_EvacuateSelector_,select_word_i),            \
+                            CAT4(_Scavenge_,size,_,ptrs))                      \
+       SPEC_COMPACTING_INFO(CAT4(_ScanLink_,size,_,ptrs),                      \
+                            CAT2(_PRStartSelector_,select_word_i),             \
+                             CAT2(_ScanMove_,size),                            \
+                            CAT2(_PRIn_,ptrs))                                 \
+       }
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[GEN_ITBL]{@GEN_x_ITBL@: Generic/general? info-tables}
+%*                                                                     *
+%************************************************************************
+
+@GEN@ info-table for non-updatable nodes (normal and non-normal forms).
+
+Size/no-of-ptrs are known at compile time, but we don't have GC
+routines wired in for those specific sizes.  Hence the size/no-of-ptrs
+is stored in the info-table.
+
+\begin{code}
+
+#define GEN_N_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Gen_N,,)               \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+       INCLUDE_GEN_INFO(size,ptrs)             \
+       }
+
+MAYBE_DECLARE_RTBL(Gen_N,,)
+
+#define GEN_N_RTBL()                                                           \
+    const W_ MK_REP_LBL(Gen_N,,)[] = {                                         \
+       INCLUDE_TYPE_INFO(GEN_N)                                                \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: in info table */      \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_S,_Scavenge_S_N)                         \
+       INCLUDE_COMPACTING_INFO(_ScanLink_S_N,_PRStart_N,_ScanMove_S,_PRIn_I)   \
+       }
+
+#define GEN_S_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Gen_S,,)               \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+       INCLUDE_GEN_INFO(size,ptrs)             \
+       }
+
+MAYBE_DECLARE_RTBL(Gen_S,,)
+
+#define GEN_S_RTBL()                                                           \
+    const W_ MK_REP_LBL(Gen_S,,)[] = {                                         \
+       INCLUDE_TYPE_INFO(GEN_S)                                                \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: in info table */      \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_S,_Scavenge_S_N)                         \
+       INCLUDE_COMPACTING_INFO(_ScanLink_S_N,_PRStart_N,_ScanMove_S,_PRIn_I)   \
+       }
+
+#ifdef PAR
+# define GEN_U_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    entry_localness(CAT2(RBH_,entry_code));            \
+    localness W_ infolbl[];                    \
+    localness W_ CAT2(RBH_,infolbl)[] = {      \
+        (W_) CAT2(RBH_,entry_code)             \
+       ,(W_) INFO_OTHER_TAG                    \
+       ,(W_) MK_REP_REF(Gen_RBH,,)             \
+       INCLUDE_PROFILING_INFO(RBH)             \
+       INCLUDE_UPDATE_INFO(INFO_UNUSED,INFO_UNUSED)    \
+       INCLUDE_GEN_INFO(size,ptrs)             \
+       INCLUDE_RBH_INFO(infolbl)               \
+       };                                      \
+    STGFUN(CAT2(RBH_,entry_code)) { JMP_(RBH_entry); }\
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Gen_U,,)               \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(INFO_UNUSED,INFO_UNUSED)    \
+       INCLUDE_GEN_INFO(size,ptrs)             \
+       INCLUDE_RBH_INFO(CAT2(RBH_,infolbl))    \
+       }
+
+MAYBE_DECLARE_RTBL(Gen_RBH,,)
+
+# define GEN_RBH_RTBL()                                                                \
+    const W_ MK_REP_LBL(Gen_RBH,,)[] = {                                       \
+       INCLUDE_TYPE_INFO(GEN_RBH)                                              \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: no size/no-ptrs! */   \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_RBH_S,_Scavenge_RBH_N)                   \
+       INCLUDE_COMPACTING_INFO(_ScanLink_RBH_N,_PRStart_RBH_N,_ScanMove_RBH_S,_PRIn_RBH_I)     \
+       }
+
+#else
+
+# define GEN_U_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Gen_U,,)               \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(INFO_UNUSED,INFO_UNUSED)    \
+       INCLUDE_GEN_INFO(size,ptrs)             \
+       }
+#endif
+
+MAYBE_DECLARE_RTBL(Gen_U,,)
+
+#define GEN_U_RTBL()                                                           \
+    const W_ MK_REP_LBL(Gen_U,,)[] = {                                         \
+       INCLUDE_TYPE_INFO(GEN_U)                                                \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: no size/no-ptrs! */   \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_S,_Scavenge_S_N)                         \
+       INCLUDE_COMPACTING_INFO(_ScanLink_S_N,_PRStart_N,_ScanMove_S,_PRIn_I)   \
+       }
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[DYN_ITBL]{Dynamic-object info tables}
+%*                                                                     *
+%************************************************************************
+
+For these, the size/no-of-pointers is not known until runtime.  E.g.,
+arrays.  Those fields are, therefore, in the closure itself, and not
+in the info table.
+
+All @DYN@ closures are @PAP@s, so they are not updatable.
+
+\begin{code}
+
+#define DYN_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_LBL(Dyn,,)                 \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       }
+
+MAYBE_DECLARE_RTBL(Dyn,,)
+
+#define DYN_RTBL()                                                     \
+    const W_ MK_REP_LBL(Dyn,,)[] = {                                   \
+       INCLUDE_TYPE_INFO(DYN)                                          \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* in closure! */    \
+       INCLUDE_PAR_INFO                                                \
+       INCLUDE_COPYING_INFO(_Evacuate_Dyn,_Scavenge_Dyn)               \
+       INCLUDE_COMPACTING_INFO(_ScanLink_Dyn,_PRStart_Dyn,_ScanMove_Dyn,_PRIn_I_Dyn) \
+       }
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[TUPLE_ITBL]{``Tuple'' and ``Data'' info-tables}
+%*                                                                     *
+%************************************************************************
+
+``Tuples'' are essentially DYNs with all pointers (no non-pointers).
+``Data things'' are DYNs with all non-pointers.
+
+\begin{code}
+
+#define TUPLE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Tuple,,)               \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       }
+
+MAYBE_DECLARE_RTBL(Tuple,,)
+
+#define TUPLE_RTBL() \
+    const W_ MK_REP_LBL(Tuple,,)[] = { \
+       INCLUDE_TYPE_INFO(TUPLE)                                        \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: in closure */ \
+       INCLUDE_PAR_INFO                                                \
+       INCLUDE_COPYING_INFO(_Evacuate_Tuple,_Scavenge_Tuple) \
+       INCLUDE_COMPACTING_INFO(_ScanLink_Tuple,_PRStart_Tuple,_ScanMove_Tuple,_PRIn_I_Tuple) \
+       }
+
+#define DATA_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Data,,)                \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       }
+
+MAYBE_DECLARE_RTBL(Data,,)
+
+#define DATA_RTBL()                    \
+    const W_ MK_REP_LBL(Data,,)[] = {  \
+       INCLUDE_TYPE_INFO(DATA)         \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: in closure */ \
+       INCLUDE_PAR_INFO                 \
+       INCLUDE_COPYING_INFO(_Evacuate_Data,_Scavenge_Data) \
+       INCLUDE_COMPACTING_INFO(_ScanLink_Data,_PRStart_Data,_ScanMove_Data,_PRIn_Error) \
+    }
+
+/* Here is the decl for the only DATA info table used! */
+#ifndef COMPILING_GHC
+EXTDATA_RO(ArrayOfData_info);
+#endif
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[MUTUPLE_ITBL]{Info-table for (im)mutable [array-ish] objects}
+%*                                                                     *
+%************************************************************************
+
+ToDo: Integrate with PAR stuff (Kevin) !!
+If someone bothers to document this I'll see what I can do! KH
+
+\begin{code}
+
+#if defined(GC_MUT_REQUIRED)
+
+# define MUTUPLE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(MuTuple,,)             \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       }
+
+MAYBE_DECLARE_RTBL(MuTuple,,)
+
+# define MUTUPLE_RTBL()                                \
+    const W_ MK_REP_LBL(MuTuple,,)[] = {       \
+       INCLUDE_TYPE_INFO(MUTUPLE)              \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: in closure! */ \
+       INCLUDE_PAR_INFO                         \
+       INCLUDE_COPYING_INFO(_Evacuate_MuTuple,_Scavenge_MuTuple) \
+       INCLUDE_COMPACTING_INFO(_ScanLink_MuTuple,_PRStart_MuTuple,_ScanMove_MuTuple,_PRIn_I_MuTuple) \
+       }
+
+# define IMMUTUPLE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(ImmuTuple,,)           \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       }
+
+MAYBE_DECLARE_RTBL(ImmuTuple,,)
+
+# define IMMUTUPLE_RTBL() \
+    const W_ MK_REP_LBL(ImmuTuple,,)[] = {  \
+       INCLUDE_TYPE_INFO(IMMUTUPLE)        \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: in closure! */ \
+       INCLUDE_PAR_INFO                         \
+       INCLUDE_COPYING_INFO(_Evacuate_MuTuple,_Scavenge_MuTuple) \
+       INCLUDE_COMPACTING_INFO(_ScanLink_MuTuple,_PRStart_MuTuple,_ScanMove_ImmuTuple,_PRIn_I_MuTuple) \
+    }
+  
+#else   /* ! GC_MUT_REQUIRED --- define as TUPLE closure */
+
+# define MUTUPLE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+       TUPLE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type)
+# define IMMUTUPLE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+       TUPLE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type)
+
+# define MUTUPLE_RTBL()
+# define IMMUTUPLE_RTBL()
+#endif
+
+/* Here are the decls for the only MUTUPLE info tables used. */
+#ifndef COMPILING_GHC
+EXTDATA_RO(ArrayOfPtrs_info);
+EXTDATA_RO(ImMutArrayOfPtrs_info);
+EXTDATA_RO(EmptySVar_info);
+EXTDATA_RO(FullSVar_info);
+#endif
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[STATIC_ITBL]{Info tables for static objects (outside the heap)}
+%*                                                                     *
+%************************************************************************
+
+Size and ptrs fields are used by interpretive code, such as @ghci@,
+the parallel Pack code (@Pack.lc@) and possibly to-be-written debug
+code.
+
+\begin{code}
+
+#define STATIC_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Static,,)              \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+       INCLUDE_STATIC_INFO(size,ptrs)          \
+       }
+
+MAYBE_DECLARE_RTBL(Static,,)
+
+#define STATIC_RTBL() \
+    const W_ MK_REP_LBL(Static,,)[] = { \
+       INCLUDE_TYPE_INFO(STATIC)       \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) /* NB: in info table! */ \
+       INCLUDE_PAR_INFO                 \
+       INCLUDE_COPYING_INFO(_Evacuate_Static,_Scavenge_Static) \
+       INCLUDE_COMPACTING_INFO(_Dummy_Static_entry,_PRStart_Static, \
+                               _Dummy_Static_entry,_Dummy_Static_entry) \
+       }
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[MallocPtr_ITBL]{@MallocPtr_TBL@: @MallocPtr@ info-table}
+%*                                                                     *
+%************************************************************************
+
+The following table is a bit like that for @SPEC@ with 0 pointers and
+a small number of non-ptrs.  However, the garbage collection routines
+are a bit special.
+
+I'm assuming @SPEC_N@, so that we don't need to pad out the info table. (JSM)
+
+\begin{code}
+#if !defined(PAR)
+
+# define MallocPtr_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(MallocPtr,,)           \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+    }
+
+MAYBE_DECLARE_RTBL(MallocPtr,,)
+
+# define MallocPtr_RTBL() \
+    const W_ MK_REP_LBL(MallocPtr,,)[] = { \
+       INCLUDE_TYPE_INFO(INTERNAL)                             \
+       INCLUDE_SIZE_INFO(MallocPtr_SIZE, 0L)                   \
+       INCLUDE_PAR_INFO                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_MallocPtr,_Scavenge_MallocPtr)   \
+       SPEC_COMPACTING_INFO(_ScanLink_MallocPtr,_PRStart_MallocPtr,_ScanMove_MallocPtr,_PRIn_0) \
+       }
+
+#endif /* !PAR */
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[BH_ITBL]{Info tables for ``black holes''}
+%*                                                                     *
+%************************************************************************
+
+Special info-table for black holes. It is possible to describe these
+using @SPEC@ closures but this requires explicit use of the value of
+@MIN_UPD_SIZE@. For now we have a special macro and code.
+
+\begin{code}
+
+#define BH_ITBL(infolbl,bh_code,kind,localness,entry_localness) \
+    entry_localness(bh_code);                  \
+    localness W_ infolbl[] = {                 \
+        (W_) bh_code                           \
+       ,(W_) INFO_OTHER_TAG                    \
+       ,(W_) MK_REP_REF(BH,kind,)              \
+       INCLUDE_PROFILING_INFO(BH)              \
+    }
+
+MAYBE_DECLARE_RTBL(BH,U,)
+MAYBE_DECLARE_RTBL(BH,N,)
+
+#define BH_U_SIZE   MIN_UPD_SIZE
+#define BH_N_SIZE   MIN_NONUPD_SIZE
+#define BH_RTBL(kind)                                                          \
+    const W_ MK_REP_LBL(BH,kind,)[] = {                                                \
+       INCLUDE_TYPE_INFO(BH)                                                   \
+       INCLUDE_SIZE_INFO(CAT3(BH_,kind,_SIZE),0L)                              \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(CAT2(_Evacuate_BH_,kind),CAT2(_Scavenge_BH_,kind)) \
+       INCLUDE_COMPACTING_INFO(CAT2(_ScanLink_BH_,kind),_PRStart_BH,           \
+                               CAT2(_ScanMove_BH_,kind),_PRIn_Error)           \
+    }
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[IND_ITBL]{Info table for indirections}
+%*                                                                     *
+%************************************************************************
+
+An indirection simply extracts the pointer from the
+@IND_CLOSURE_PTR(closure)@ field. The garbage collection routines will
+short out the indirection.
+\begin{code}
+
+#define IND_ITBL(infolbl,ind_code,localness,entry_localness) \
+    CAT_DECLARE(infolbl,INTERNAL_KIND,"IND","IND")     \
+    entry_localness(ind_code);                         \
+    localness W_ infolbl[] = {                         \
+        (W_) ind_code                                  \
+       ,(W_) INFO_IND_TAG                              \
+       ,(W_) MK_REP_REF(Ind,,)                         \
+       INCLUDE_PROFILING_INFO(infolbl)                 \
+       }
+
+MAYBE_DECLARE_RTBL(Ind,,)
+
+#define IND_RTBL()                                                             \
+    const W_ MK_REP_LBL(Ind,,)[] = {                                           \
+       INCLUDE_TYPE_INFO(IND)                                                  \
+       INCLUDE_SIZE_INFO(MIN_UPD_SIZE,INFO_UNUSED) /* #ptrs not here! */       \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_Ind,_Scavenge_Ind)                       \
+       INCLUDE_COMPACTING_INFO(_Dummy_Ind_entry,_PRStart_Ind,                  \
+                               _Dummy_Ind_entry,_Dummy_Ind_entry)              \
+    }
+
+\end{code}
+
+Lexical-scoped profiling (now more-or-less the default... 94/06)
+requires a special permanent indirection for PAP closures.  These 
+look exactly like regular indirections, but they are not short-circuited
+on garbage collection.
+
+\begin{code}
+#if defined(USE_COST_CENTRES)
+
+# define PERM_IND_ITBL(infolbl,ind_code,localness,entry_localness) \
+    entry_localness(ind_code);                         \
+    CAT_DECLARE(infolbl,INTERNAL_KIND,"IND","IND")     \
+    localness W_ infolbl[] = {                         \
+        (W_) ind_code                                  \
+       ,(W_) INFO_IND_TAG                              \
+       ,(W_) MK_REP_REF(Perm_Ind,,)                    \
+       INCLUDE_PROFILING_INFO(infolbl)                 \
+    }
+
+MAYBE_DECLARE_RTBL(Perm_Ind,,)
+
+# define PERM_IND_RTBL()                                                       \
+    const W_ MK_REP_LBL(Perm_Ind,,)[] = {                                      \
+       INCLUDE_TYPE_INFO(IND)                                                  \
+       INCLUDE_SIZE_INFO(MIN_UPD_SIZE,INFO_UNUSED) /* #ptrs not here! */       \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_PI,_Scavenge_PI)                         \
+       SPEC_COMPACTING_INFO(_ScanLink_PI,_PRStart_PI,                          \
+                            _ScanMove_PI,_PRIn_PI)                             \
+       }
+
+#else
+# define PERM_IND_RTBL()
+#endif
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[CAF_ITBL]{Info table for updated @CAF@s}
+%*                                                                     *
+%************************************************************************
+
+Garbage collection of @CAF@s is tricky.  We have to cope with explicit
+collection from the @CAFlist@ as well as potential references from the
+stack and heap which will cause the @CAF@ evacuation code to be
+called.  They are treated like indirections which are shorted out.
+However they must also be updated to point to the new location of the
+new closure as the @CAF@ may still be used by references which
+reside in the code.
+
+\subsubsection{Copying Collection}
+
+A first scheme might use evacuation code which evacuates the reference
+and updates the indirection. This is no good as subsequent evacuations
+will result in an already evacuated closure being evacuated. This will
+leave a forward reference in to-space!
+
+An alternative scheme evacuates the @CAFlist@ first. The closures
+referenced are evacuated and the @CAF@ indirection updated to point to
+the evacuated closure. The @CAF@ evacuation code simply returns the
+updated indirection pointer --- the pointer to the evacuated closure.
+Unfortunately the closure the @CAF@ references may be a static
+closure, in fact, it may be another @CAF@. This will cause the second
+@CAF@'s evacuation code to be called before the @CAF@ has been
+evacuated, returning an unevacuated pointer.
+
+Another scheme leaves updating the @CAF@ indirections to the end
+of the garbage collection.
+All the references are evacuated and scavenged as usual (including the
+@CAFlist@). Once collection is complete the @CAFlist@ is traversed
+updating the @CAF@ references with the result of evacuating the
+referenced closure again. This will immediately return as it must be a
+forward reference, a static closure, or a @CAF@ which will indirect by
+evacuating its reference.
+
+The crux of the problem is that the @CAF@ evacuation code needs to
+know if its reference has already been evacuated and updated. If not, then
+the reference can be evacuated, updated and returned safely (possibly
+evacuating another @CAF@). If it has, then the updated reference can be
+returned. This can be done using two @CAF@ info-tables. At the start
+of a collection the @CAFlist@ is traversed and set to an internal {\em
+evacuate and update} info-table. During collection, evacution of such a
+@CAF@ also results in the info-table being reset back to the standard
+@CAF@ {\em return reference} info-table. Thus subsequent evacuations
+will simply return the updated reference. On completion of the
+collection all @CAF@s will have {\em return reference} info-tables
+again.
+
+This is the scheme we adopt. A @CAF@ indirection has evacuation code
+which returns the evacuated and updated reference. During garbage
+collection all the @CAF@s are overwritten with an internal @CAF@ info
+table which has evacuation code which performs this evacuate and
+update and restores the original @CAF@ code. At some point during the
+collection we must ensure that all the @CAF@s are indeed
+evacuated.
+
+The only potential problem with this scheme is a cyclic list of @CAF@s
+all directly referencing (possibly via indirections) another @CAF@!
+Evacuation of the first @CAF@ will fail in an infinite loop of @CAF@
+evacuations. This is solved by ensuring that the @CAF@ info-table is
+updated to a {\em return reference} info-table before performing the
+evacuate and update. If this {\em return reference} evacuation code is
+called before the actual evacuation is complete it must be because
+such a cycle of references exists. Returning the still unevacuated
+reference is OK --- all the @CAF@s will now reference the same
+@CAF@ which will reference itself! Construction of such a structure
+indicates the program must be in an infinite loop.
+
+
+\subsubsection{Compacting Collector}
+
+When shorting out a @CAF@, its reference must be marked. A first attempt
+might explicitly mark the @CAF@s, updating the reference with the
+marked reference (possibly short circuting indirections). The actual
+@CAF@ marking code can indicate that they have already been marked
+(though this might not have actually been done yet) and return the
+indirection pointer so it is shorted out. Unfortunately the @CAF@
+reference might point to an indirection which will be subsequently
+shorted out. Rather than returning the @CAF@ reference we treat the
+@CAF@ as an indirection, calling the mark code of the reference, which
+will return the appropriately shorted reference.
+
+Problem: Cyclic list of @CAF@s all directly referencing (possibly via
+indirections) another @CAF@!
+
+Before compacting, the locations of the @CAF@ references are
+explicitly linked to the closures they reference (if they reference
+heap allocated closures) so that the compacting process will update
+them to the closure's new location. Unfortunately these locations'
+@CAF@ indirections are static.  This causes premature termination
+since the test to find the info pointer at the end of the location
+list will match more than one value.  This can be solved by using an
+auxiliary dynamic array (on the top of the A stack).  One location for
+each @CAF@ indirection is linked to the closure that the @CAF@
+references. Once collection is complete this array is traversed and
+the corresponding @CAF@ is then updated with the updated pointer from
+the auxiliary array.
+
+\begin{code}
+
+#define CAF_ITBL(infolbl,ind_code,localness,entry_localness) \
+    CAT_DECLARE(infolbl,INTERNAL_KIND,"CAF","CAF")     \
+    entry_localness(ind_code);                         \
+    localness W_ infolbl[] = {                         \
+        (W_) ind_code                                  \
+       ,(W_) INFO_IND_TAG                              \
+       ,(W_) MK_REP_REF(Caf,,)                         \
+       INCLUDE_PROFILING_INFO(infolbl)                 \
+    }
+
+MAYBE_DECLARE_RTBL(Caf,,)
+
+#define CAF_RTBL()                                                             \
+    const W_ MK_REP_LBL(Caf,,)[] = {                                           \
+       INCLUDE_TYPE_INFO(CAF)                                                  \
+       INCLUDE_SIZE_INFO(MIN_UPD_SIZE,INFO_UNUSED) /* #ptrs not here! */       \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO(_Evacuate_Caf,_Scavenge_Caf)                       \
+       INCLUDE_COMPACTING_INFO(_Dummy_Caf_entry,_PRStart_Caf,                  \
+                               _Dummy_Caf_entry,_Dummy_Caf_entry)              \
+       }
+\end{code}
+
+
+It is possible to use an alternative marking scheme, using a similar
+idea to the copying solution. This scheme avoids the need to update
+the @CAF@ references explicitly. We introduce an auxillary {\em mark
+and update} @CAF@ info-table which is used to update all @CAF@s at the
+start of a collection. The new code marks the @CAF@ reference,
+updating it with the returned reference.  The returned reference is
+itself returned so the @CAF@ is shorted out.  The code also modifies the
+@CAF@ info-table to be a {\em return reference}.  Subsequent attempts to
+mark the @CAF@ simply return the updated reference.
+
+A cyclic @CAF@ reference will result in an attempt to mark the @CAF@
+before the marking has been completed and the reference updated. We
+cannot start marking the @CAF@ as it is already being marked. Nor can
+we return the reference as it has not yet been updated. Neither can we
+treat the CAF as an indirection since the @CAF@ reference has been
+obscured by the pointer reversal stack. All we can do is return the
+@CAF@ itself. This will result in some @CAF@ references not being
+shorted out.
+
+This scheme has not been adopted but has been implemented. The code is
+commented out with @#if 0@.
+
+%************************************************************************
+%*                                                                     *
+\subsection[CONST_ITBL]{@CONST_ITBL@}
+%*                                                                     *
+%************************************************************************
+
+This declares an info table for @CONST@ closures (size 0). 
+It is the info table for a dynamicaly-allocated closure which
+will redirect references to the corresponding
+static closure @<infolbl>_closure@ during garbage collection. 
+A pointer to the static closure is kept in the info table.  (It is
+assumed that this closure is declared elsewhere.)
+
+Why do such @CONST@ objects ever exist?  Why don't we just use the static
+object in the first place?  @CONST@ objects are used only for updating
+existing objects.  We could use an indirection, but that risks costing
+extra run-time indirections until the next gc shorts it out.  So
+we update with a @CONST@, and the next gc gets rid of it.
+
+\begin{code}
+
+#define CONST_ITBL(infolbl,closurelbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    EXTDATA(closurelbl);                       \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) tag                               \
+       ,(W_) MK_REP_REF(Const,,)               \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+       INCLUDE_CONST_INFO(closurelbl)          \
+       }
+
+MAYBE_DECLARE_RTBL(Const,,)
+
+#define CONST_RTBL()                                                   \
+    const W_ MK_REP_LBL(Const,,)[] = {                                 \
+       INCLUDE_TYPE_INFO(CONST)                                        \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED)                      \
+       INCLUDE_PAR_INFO                                                \
+       INCLUDE_COPYING_INFO_CONST(_Evacuate_Const,_Scavenge_Const)     \
+       INCLUDE_COMPACTING_INFO(_Dummy_Const_entry,_PRStart_Const,      \
+                               _Dummy_Const_entry,_Dummy_Const_entry)  \
+    }
+
+\end{code}
+
+This builds an info-table which will have pointers to the closure
+replaced with @closure_lbl@ during garbage collection. @closure_lbl@
+must be the label of a static closure, whose entry code has identical
+behaviour to that in the corresponding @CONST_ITBL@.  Usually
+the info pointer of this closure will be the very one defined by this
+macro!
+
+These closures always consist only of an info pointer; that is, its
+size is zero.
+
+A copying collection implements this with evacuation code which
+returns @closure_lbl@, without actually evacuating the object at all.
+A compacting collector uses marking code which returns
+@closure_lbl@, without marking the closure.
+
+%************************************************************************
+%*                                                                     *
+\subsection[FOOLIKE_ITBL]{``Char-like'' and ``Int-like'' info-tables}
+%*                                                                     *
+%************************************************************************
+
+Char-like: This builds an info-table which, when GC happens, will have
+pointers to the closure replaced with the appropriate element of the
+@CHARLIKE_closures@ array.
+
+\begin{code}
+
+#define CHARLIKE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*tag,size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) INFO_FIRST_TAG                    \
+       ,(W_) MK_REP_REF(CharLike,,)            \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+       }
+
+MAYBE_DECLARE_RTBL(CharLike,,)
+
+#define CHARLIKE_RTBL()                                                                \
+    const W_ MK_REP_LBL(CharLike,,)[] = {                                      \
+       INCLUDE_TYPE_INFO(CHARLIKE)                                             \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED)                              \
+       INCLUDE_PAR_INFO                                                        \
+       INCLUDE_COPYING_INFO_CONST(_Evacuate_CharLike,_Scavenge_CharLike)       \
+       INCLUDE_COMPACTING_INFO(_Dummy_CharLike_entry,_PRStart_CharLike,        \
+                               _Dummy_CharLike_entry,_Dummy_CharLike_entry)    \
+       }
+
+\end{code}
+
+
+Int-like: this builds the info-table required for intlike closures.
+The normal heap-allocated info-table for fixed-size integers (size
+@1@); it is used for updates too. 
+At GC, this is redirected to a static intlike closure if one is
+available.
+
+Note again the sneaky hiding of a reference to the real info-table in
+the part of the info-table that normally holds the size of the
+closure.
+THIS CHANGES IN THE COMMONED INFO-TABLE WORLD.
+
+\begin{code}
+
+#define INTLIKE_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*tag,size,ptrs unused*/ \
+    CAT_DECLARE(infolbl,kind,descr,type)       \
+    entry_localness(entry_code);               \
+    localness W_ infolbl[] = {                 \
+        (W_) entry_code                                \
+       ,(W_) INFO_FIRST_TAG                    \
+       ,(W_) MK_REP_REF(IntLike,,)             \
+       INCLUDE_PROFILING_INFO(infolbl)         \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+    }
+
+MAYBE_DECLARE_RTBL(IntLike,,)
+
+#define INTLIKE_RTBL()                                                 \
+    const W_ MK_REP_LBL(IntLike,,)[] = {                               \
+       INCLUDE_TYPE_INFO(INTLIKE)                                      \
+       INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED)                      \
+       INCLUDE_PAR_INFO                                                \
+       INCLUDE_COPYING_INFO_CONST(_Evacuate_IntLike,_Scavenge_1_0)     \
+       INCLUDE_COMPACTING_INFO(_ScanLink_1_0,_PRStart_IntLike,         \
+                               _ScanMove_1,_PRIn_Error)                \
+    }
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[INREGS_ITBL]{@INREGS_ITBL@s}
+%*                                                                     *
+%************************************************************************
+
+The emaciated info table for a phantom closure that lives only in regs.
+We don't need any GC information, because these closures never make it into
+the heap (not with this info table, anyway).  Similarly, we don't need an
+entry address, because these closures are never entered...they only exist
+during a return.
+
+\begin{code}
+
+#define INREGS_ITBL(infolbl,entry_code,upd_code,liveness,tag,size,ptrs,localness,entry_localness,kind,descr,type) /*mostly unused*/ \
+    localness W_ infolbl[] = {                 \
+        (W_) INFO_UNUSED                       \
+       ,(W_) tag                               \
+       ,(W_) INFO_UNUSED                       \
+       INREGS_PROFILING_INFO                   \
+       INCLUDE_UPDATE_INFO(upd_code,liveness)  \
+    }
+
+/* Declare the phantom info table vectors (just Bool at the moment) */
+#ifndef COMPILING_GHC
+EXTDATA_RO(Bool_itblvtbl);
+#endif
+
+\end{code}
+
+End multi-slurp protection:
+\begin{code}
+#endif /* SMInfoTables_H */
+\end{code}