% % (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[@ForeignObj_K@] A Foreign object (non-Haskell heap resident). \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(PROFILING) # 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} #if defined(PAR) || defined(GRAN) # 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)); #if defined(GRAN) void convertFromRBH PROTO((P_ closure)); #elif defined(PAR) void convertToFetchMe PROTO((P_ closure, globalAddr *ga)); #endif #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< 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)) \ } #if defined(PAR) || defined(GRAN) # 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} #if defined(PAR) || defined(GRAN) # 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) \ } #if defined(PAR) || defined(GRAN) # 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,_Dummy_Static_entry) \ INCLUDE_COMPACTING_INFO(_Dummy_Static_entry,_PRStart_Static, \ _Dummy_Static_entry,_Dummy_Static_entry) \ } \end{code} %************************************************************************ %* * \subsection[ForeignObj_ITBL]{@ForeignObj_TBL@: @ForeignObj@ 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 ForeignObj_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(ForeignObj,,) \ INCLUDE_PROFILING_INFO(infolbl) \ } MAYBE_DECLARE_RTBL(ForeignObj,,) # define ForeignObj_RTBL() \ const W_ MK_REP_LBL(ForeignObj,,)[] = { \ INCLUDE_TYPE_INFO(INTERNAL) \ INCLUDE_SIZE_INFO(ForeignObj_SIZE, 0L) \ INCLUDE_PAR_INFO \ INCLUDE_COPYING_INFO(_Evacuate_ForeignObj,_Scavenge_ForeignObj) \ SPEC_COMPACTING_INFO(_ScanLink_ForeignObj,_PRStart_ForeignObj,_ScanMove_ForeignObj,_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_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 (normally). \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(PROFILING) || defined(TICKY_TICKY) # 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@ 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 @_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,,) #ifdef TICKY_TICKY /* we need real routines if we may not be commoning up */ #define CONST_Scav _Scavenge_0_0 #define CONST_Link _ScanLink_0_0 #define CONST_Move _ScanMove_0 #else #define CONST_Scav _Dummy_Const_entry #define CONST_Link _Dummy_Const_entry #define CONST_Move _Dummy_Const_entry #endif #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(_Evacuate_Const,CONST_Scav) \ INCLUDE_COMPACTING_INFO(CONST_Link,_PRStart_Const, \ CONST_Move,_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,,) #ifdef TICKY_TICKY /* we need real routines if we may not be commoning up */ #define CHARLIKE_Scav _Scavenge_1_0 #define CHARLIKE_Link _ScanLink_1_0 #define CHARLIKE_Move _ScanMove_1 #else #define CHARLIKE_Scav _Dummy_CharLike_entry #define CHARLIKE_Link _Dummy_CharLike_entry #define CHARLIKE_Move _Dummy_CharLike_entry #endif #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(_Evacuate_CharLike,CHARLIKE_Scav) \ INCLUDE_COMPACTING_INFO(CHARLIKE_Link,_PRStart_CharLike, \ CHARLIKE_Move,_PRIn_Error) \ } \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. \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(_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(Prelude_Bool_itblvtbl); #endif \end{code} End multi-slurp protection: \begin{code} #endif /* SMInfoTables_H */ \end{code}