% % (c) The GRASP Project, Glasgow University, 1993 % \section[StgRegs-decls]{STG-machine register mappings} \begin{code} #ifndef COPTREGS_H #define COPTREGS_H #include "StgMachDeps.h" #include "StgTypes.h" #include "MachRegs.h" #define GLOBAL_REG_DECL(type,name,reg) register type name REG(reg); \end{code} Various parts of the GHC system use various sets of ``registers,'' by which we mean (frequently-used) words of globally-visible information. For example, the everyday ``Haskell threaded world,'' uses the ``registers'' @Hp@, @R4@, etc., etc. We would really like to ``steal'' machine registers from the C execution model (via GCC's global-variable-in-register extension) and map some/all of our ``STG registers'' onto real machine registers. This has a profound benefit in terms of execution speed. This file/document/section sets out the various (machine-dependent) mappings that we use. Note: for one machine, there are {\em several} possible register mappings, {\em one} of which is in force at any time. Obviously, the ``main'' mapping (used in the Haskell threaded world) dominates, but when garbage-collecting (for example), we'd rather not tie up all those registers in that way (i.e., for global-variables that aren't used in the GC). Instead, we'd rather bring in {\em another} register mapping, tuned to the needs of a particular (isolated) bit of C code. As there are several garbage collectors there are quite a few possible mappings. %************************************************************************ %* * \subsection[saved-STG-regs]{Saved STG registers} %* * %************************************************************************ The following stuff is available regardless of register map. It allows us access to the saved STG registers from other parts of the RTS (notably from the storage manager). \begin{code} typedef struct rt { StgDouble rDbl[2]; /* Put a double first to ensure expected alignment */ StgFloat rFlt[4]; StgUnion rR[8]; PP_ rSpA; PP_ rSuA; P_ rSpB; P_ rSuB; P_ rHp; P_ rHpLim; I_ rTag; StgRetAddr rRet; I_ rActivity; /* NB: UNUSED */ P_ rCstkptr; /* used for iX86 registerizing only! offset=100 */ P_ rWrapReturn; /* ditto; offset=104 */ P_ rSaveECX; /* ditto; offset=108 */ #if defined(CONCURRENT) P_ rStkO; I_ rLiveness; #endif } STGRegisterTable; \end{code} There are several confusing macro sets for accessing STG registers at various stages in their lives. The MAIN_* macros refer to the save locations for the main thread. These are generally useful when the main thread is suspended. Note that the save locations for S[up][AB] are actually in the pseudo stack object, MainStkO, when running threads. The SAVE_* macros refer to the save locations for the current thread, without using BaseReg. These are used when we cannot be sure that any STG registers are actually loaded in machine registers. The RTBL_* macros refer to the register table locations for the current thread, indexed from BaseReg. If BaseReg is in a machine register, that register {\em must} be loaded with the address of the register table. OK, now... In the sequential world at least, each of those ``register'' declarations also set up a ``backup'' location; for register @r@, the backup location (a global variable) is @r_SAVE@. We need @SAVE_STG_REGS@ and @RESTORE_STG_REGS@ macros, which copy back and forth between the ``registers'' and their \tr{*_SAVE} backup locations. In the parallel world, we have the closely-related business of saving/restoring ``thread state''. We do it in two stages: save/restore to/from \tr{*_SAVE} locations, then fill in the ``thread-state object'' (TSO) from the \tr{*_SAVE} locations. (This means the thread-state saving can more easily be written in C, rather than assembler.) Why no space to save BaseReg? Because either (1) if in a caller-save register, the caller will have saved it; or (2) if in a callee-save register, the miniInterpret machinery will have saved it. This works because we entered ``threaded Haskell land'' in a v disciplined way---i.e., via miniInterpret. However, the bits of code that use the various GC register maps (SCAV, MARK, SCAN) are called in less-disciplined ways, so their base-regs need saving/restoring. (WDP 95/02) \begin{code} #ifndef PAR extern STGRegisterTable MainRegTable; #endif /* PAR */ /* these are for the main register table */ #define MAIN_R1 (MainRegTable.rR[0]) #define MAIN_R2 (MainRegTable.rR[1]) #define MAIN_R3 (MainRegTable.rR[2]) #define MAIN_R4 (MainRegTable.rR[3]) #define MAIN_R5 (MainRegTable.rR[4]) #define MAIN_R6 (MainRegTable.rR[5]) #define MAIN_R7 (MainRegTable.rR[6]) #define MAIN_R8 (MainRegTable.rR[7]) #define MAIN_Flt1 (MainRegTable.rFlt[0]) #define MAIN_Flt2 (MainRegTable.rFlt[1]) #define MAIN_Flt3 (MainRegTable.rFlt[2]) #define MAIN_Flt4 (MainRegTable.rFlt[3]) #define MAIN_Dbl1 (MainRegTable.rDbl[0]) #define MAIN_Dbl2 (MainRegTable.rDbl[1]) #define MAIN_Hp (MainRegTable.rHp) #define MAIN_HpLim (MainRegTable.rHpLim) #define MAIN_Tag (MainRegTable.rTag) #define MAIN_Ret (MainRegTable.rRet) #define MAIN_StkO (MainStkO) #define MAIN_Liveness (MainRegTable.rLiveness) #ifndef CONCURRENT #define MAIN_SpA (MainRegTable.rSpA) #define MAIN_SuA (MainRegTable.rSuA) #define MAIN_SpB (MainRegTable.rSpB) #define MAIN_SuB (MainRegTable.rSuB) /* these are really for *SAVE*ing */ #define SAVE_R1 MAIN_R1 #define SAVE_R2 MAIN_R2 #define SAVE_R3 MAIN_R3 #define SAVE_R4 MAIN_R4 #define SAVE_R5 MAIN_R5 #define SAVE_R6 MAIN_R6 #define SAVE_R7 MAIN_R7 #define SAVE_R8 MAIN_R8 #define SAVE_Flt1 MAIN_Flt1 #define SAVE_Flt2 MAIN_Flt2 #define SAVE_Flt3 MAIN_Flt3 #define SAVE_Flt4 MAIN_Flt4 #define SAVE_Dbl1 MAIN_Dbl1 #define SAVE_Dbl2 MAIN_Dbl2 #define SAVE_SpA MAIN_SpA #define SAVE_SuA MAIN_SuA #define SAVE_SpB MAIN_SpB #define SAVE_SuB MAIN_SuB #define SAVE_Tag MAIN_Tag #define SAVE_Ret MAIN_Ret #else extern P_ MainStkO; #define MAIN_SpA (STKO_SpA(MainStkO)) #define MAIN_SuA (STKO_SuA(MainStkO)) #define MAIN_SpB (STKO_SpB(MainStkO)) #define MAIN_SuB (STKO_SuB(MainStkO)) extern STGRegisterTable *CurrentRegTable; /* these are really for *SAVE*ing */ #define SAVE_R1 (CurrentRegTable->rR[0]) #define SAVE_R2 (CurrentRegTable->rR[1]) #define SAVE_R3 (CurrentRegTable->rR[2]) #define SAVE_R4 (CurrentRegTable->rR[3]) #define SAVE_R5 (CurrentRegTable->rR[4]) #define SAVE_R6 (CurrentRegTable->rR[5]) #define SAVE_R7 (CurrentRegTable->rR[6]) #define SAVE_R8 (CurrentRegTable->rR[7]) #define SAVE_Flt1 (CurrentRegTable->rFlt[0]) #define SAVE_Flt2 (CurrentRegTable->rFlt[1]) #define SAVE_Flt3 (CurrentRegTable->rFlt[2]) #define SAVE_Flt4 (CurrentRegTable->rFlt[3]) #define SAVE_Dbl1 (CurrentRegTable->rDbl[0]) #define SAVE_Dbl2 (CurrentRegTable->rDbl[1]) /* These are only valid when StkOReg is loaded! */ #define SAVE_SpA (STKO_SpA(StkOReg)) #define SAVE_SuA (STKO_SuA(StkOReg)) #define SAVE_SpB (STKO_SpB(StkOReg)) #define SAVE_SuB (STKO_SuB(StkOReg)) #define SAVE_Tag (CurrentRegTable->rTag) #define SAVE_Ret (CurrentRegTable->rRet) #define SAVE_StkO (CurrentRegTable->rStkO) #define SAVE_Liveness (CurrentRegTable->rLiveness) #endif /* CONCURRENT */ /* Note that the SAVE_ locations for the Hp registers are in the smInfo structure */ #define SAVE_Hp (StorageMgrInfo.hp) #define SAVE_HpLim (StorageMgrInfo.hplim) \end{code} %************************************************************************ %* * \subsection[null-mapping-StgRegs]{The empty register mapping} %* * %************************************************************************ This mapping leaves all machine registers free for normal C allocation. In the RTS, this is the preferred mapping, because it allows gcc to use all available registers, with the normal callee-saves conventions. \begin{code} #if defined(NULL_REG_MAP) #else \end{code} This is a HACK here; see comment in COptJumps.lh. \begin{code} #if alpha_dec_osf1_TARGET && defined(__STG_TAILJUMPS__) && defined(__GNUC__) register void *_procedure __asm__("$27"); #endif #if (mipsel_TARGET_ARCH || mipseb_TARGET_ARCH) && defined(__STG_TAILJUMPS__) && defined(__GNUC__) register void *_procedure __asm__("$25"); #endif \end{code} %************************************************************************ %* * \subsection[mark-mapping-StgRegs]{The ``mark'' register mapping} %* * %************************************************************************ The mark mapping is used for pointer-reversal marking during GC. It is used by most of the current garbage collectors. \begin{code} #if defined(MARK_REG_MAP) \end{code} Mark (GC) register mapping: \begin{verbatim} sparc m68k alpha mipseX hppa iX86 powerpc ----- ---- ----- ------ ---- ---- ------- MarkBase ebx Mark i0 a2 $9 $16 r4 ebp MStack i1 a3 $10 $17 r5 esi MRoot i2 a4 $11 $18 r6 edi BitArray i3 a5 $12 $19 r7 HeapBase i4 d3 $13 $20 r8 HeapLim i5 d4 $14 $21 r9 \end{verbatim} \begin{code} typedef struct { P_ rMark; P_ rMStack; P_ rMRoot; BitWord *rBitArray; P_ rHeapBase; P_ rHeapLim; P_ rMarkBase; } RegisterTable; #define REGDUMP(dump) static RegisterTable dump #define SAVE_Mark (MarkRegTable.rMark) #define SAVE_MStack (MarkRegTable.rMStack) #define SAVE_MRoot (MarkRegTable.rMRoot) #define SAVE_BitArray (MarkRegTable.rBitArray) #define SAVE_HeapBase (MarkRegTable.rHeapBase) #define SAVE_HeapLim (MarkRegTable.rHeapLim) extern RegisterTable MarkRegTable; #ifdef REG_MarkBase GLOBAL_REG_DECL(RegisterTable *,MarkBaseReg,REG_MarkBase) #else #define MarkBaseReg (&MarkRegTable) #endif #ifdef REG_Mark GLOBAL_REG_DECL(P_,Mark,REG_Mark) #else #define Mark SAVE_Mark #endif #ifdef REG_MStack GLOBAL_REG_DECL(P_,MStack,REG_MStack) #else #define MStack SAVE_MStack #endif #ifdef REG_MRoot GLOBAL_REG_DECL(P_,MRoot,REG_MRoot) #else #define MRoot SAVE_MRoot #endif #ifdef REG_BitArray GLOBAL_REG_DECL(P_,BitArray,REG_BitArray) #else #define BitArray SAVE_BitArray #endif #ifdef REG_HeapBase GLOBAL_REG_DECL(P_,HeapBase,REG_HeapBase) #else #define HeapBase SAVE_HeapBase #endif #ifdef REG_HeapLim GLOBAL_REG_DECL(P_,HeapLim,REG_HeapLim) #else #define HeapLim SAVE_HeapLim #endif #if defined(__STG_GCC_REGS__) /* Keep -Wmissing-prototypes from complaining */ void SAVE_REGS PROTO((RegisterTable *dump)); void RESTORE_REGS PROTO((RegisterTable *dump)); extern STG_INLINE void SAVE_REGS(dump) RegisterTable *dump; { #ifdef REG_MarkBase dump->rMarkBase = (P_) MarkBaseReg; /* save whatever is in it */ MarkBaseReg = dump; /* set it correctly */ #endif #ifdef REG_Mark dump->rMark = Mark; #endif #ifdef REG_MStack dump->rMStack = MStack; #endif #ifdef REG_MRoot dump->rMRoot = MRoot; #endif #ifdef REG_BitArray dump->rBitArray = BitArray; #endif #ifdef REG_HeapBase dump->rHeapBase = HeapBase; #endif #ifdef REG_HeapLim dump->rHeapLim = HeapLim; #endif } extern STG_INLINE void RESTORE_REGS(dump) RegisterTable *dump; { #ifdef REG_Mark Mark = dump->rMark; #endif #ifdef REG_MStack MStack = dump->rMStack; #endif #ifdef REG_MRoot MRoot = dump->rMRoot; #endif #ifdef REG_BitArray BitArray = dump->rBitArray; #endif #ifdef REG_HeapBase HeapBase = dump->rHeapBase; #endif #ifdef REG_HeapLim HeapLim = dump->rHeapLim; #endif #ifdef REG_MarkBase MarkBaseReg = (RegisterTable *) dump->rMarkBase; /* restore to whatever it was */ #endif } #else #define SAVE_REGS(dump) #define RESTORE_REGS(dump) #endif \end{code} %************************************************************************ %* * \subsection[scan-mapping-StgRegs]{The ``scan'' register mapping} %* * %************************************************************************ The scan mapping is used for all of the in-place garbage collectors. On architectures with register windows, like the SPARC, these must reside in global registers, because the scan code is not threaded. \begin{code} #else #if defined(SCAN_REG_MAP) \end{code} Scan (GC) register mapping: \begin{verbatim} sparc m68k alpha mipseX hppa iX86 powerpc ----- ---- ----- ------ ---- ---- ------- ScanBase g4 Scan a2 $9 $16 r4 ebx New a3 $10 $17 r5 ebp LinkLim a4 $11 $18 r6 esi \end{verbatim} \begin{code} typedef struct { P_ rScan; P_ rNew; P_ rLinkLim; P_ rScanBase; } RegisterTable; #define REGDUMP(dump) static RegisterTable dump #define SAVE_Scan (ScanRegTable.rScan) #define SAVE_New (ScanRegTable.rNew) #define SAVE_LinkLim (ScanRegTable.rLinkLim) extern RegisterTable ScanRegTable; #ifdef REG_ScanBase GLOBAL_REG_DECL(RegisterTable *,ScanBaseReg,REG_ScanBase) #else #define ScanBaseReg (&ScanRegTable) #endif #ifdef REG_Scan GLOBAL_REG_DECL(P_,Scan,REG_Scan) #else # ifdef REG_ScanBase # define Scan (ScanBaseReg->rScan) # else # define Scan SAVE_Scan # endif #endif #ifdef REG_New GLOBAL_REG_DECL(P_,New,REG_New) #else # ifdef REG_ScanBase # define New (ScanBaseReg->rNew) # else # define New SAVE_New # endif #endif #ifdef REG_LinkLim GLOBAL_REG_DECL(P_,LinkLim,REG_LinkLim) #else # ifdef REG_ScanBase # define LinkLim (ScanBaseReg->rLinkLim) # else # define LinkLim SAVE_LinkLim # endif #endif #if defined(__STG_GCC_REGS__) /* Keep -Wmissing-prototypes from complaining */ void SAVE_REGS PROTO((RegisterTable *dump)); void RESTORE_REGS PROTO((RegisterTable *dump)); extern STG_INLINE void SAVE_REGS(dump) RegisterTable *dump; { #ifdef REG_ScanBase dump->rScanBase = (P_) ScanBaseReg; /* save whatever is in it */ ScanBaseReg = dump; /* set it correctly */ #endif #ifdef REG_Scan dump->rScan = Scan; #endif #ifdef REG_New dump->rNew = New; #endif #ifdef REG_LinkLim dump->rLinkLim = LinkLim; #endif } extern STG_INLINE void RESTORE_REGS(dump) RegisterTable *dump; { #ifdef REG_Scan Scan = dump->rScan; #endif #ifdef REG_New New = dump->rNew; #endif #ifdef REG_LinkLim LinkLim = dump->rLinkLim; #endif #ifdef REG_ScanBase ScanBaseReg = (RegisterTable *) dump->rScanBase; /* restore to whatever it was */ #endif } #else #define SAVE_REGS(dump) #define RESTORE_REGS(dump) #endif \end{code} %************************************************************************ %* * \subsection[scav-mapping-StgRegs]{The ``scavenge'' register mapping} %* * %************************************************************************ The scan mapping is used for all of the in-place garbage collectors. (I believe that it must use a subset of the registers that are used in the mark mapping, but I could be wrong. --JSM) Note: registers must not be mangled by sliding register windows, etc. or there'll be trouble. ADR \begin{code} #else #if defined(SCAV_REG_MAP) \end{code} Scavenge (GC) register mapping: \begin{verbatim} sparc m68k alpha mipseX hppa iX86 powerpc ----- ---- ----- ------ ---- ---- ------- ScavBase g4 Scav a2 $9 $16 r4 ebx ToHp a3 $10 $17 r5 ebp OldGen (gn/ap) a4 $11 $18 r6 esi AllocGen (gn) a5 OldHp (gn) d3 \end{verbatim} (Calling this struct @ScavRegisterTable@ would make it possible for @gdb@ to display it properly. At the moment, @gdb@ confuses it with the scan register table etc. ADR ) \begin{code} typedef struct { P_ rScav; P_ rToHp; P_ rOldGen; #ifdef GCgn P_ rAllocGen; P_ rOldHp; #endif P_ rScavBase; } RegisterTable; #define REGDUMP(dump) static RegisterTable dump #define SAVE_Scav (ScavRegTable.rScav) #define SAVE_ToHp (ScavRegTable.rToHp) #define SAVE_OldGen (ScavRegTable.rOldGen) #define SAVE_AllocGen (ScavRegTable.rAllocGen) #define SAVE_OldHp (ScavRegTable.rOldHp) extern RegisterTable ScavRegTable; #ifdef REG_ScavBase GLOBAL_REG_DECL(RegisterTable *,ScavBaseReg,REG_ScavBase) #else #define ScavBaseReg (&ScavRegTable) #endif #ifdef REG_Scav GLOBAL_REG_DECL(P_,Scav,REG_Scav) #else # ifdef REG_ScavBase # define Scav (ScavBaseReg->rScav) # else # define Scav SAVE_Scav # endif #endif #ifdef REG_ToHp GLOBAL_REG_DECL(P_,ToHp,REG_ToHp) #else # ifdef REG_ScavBase # define ToHp (ScavBaseReg->rToHp) # else # define ToHp SAVE_ToHp # endif #endif #ifdef REG_OldGen GLOBAL_REG_DECL(P_,OldGen,REG_OldGen) #else # ifdef REG_ScavBase # define OldGen (ScavBaseReg->rOldGen) # else # define OldGen SAVE_OldGen # endif #endif #ifdef REG_AllocGen GLOBAL_REG_DECL(P_,AllocGen,REG_AllocGen) #else # ifdef REG_ScavBase # define AllocGen (ScavBaseReg->rAllocGen) # else # define AllocGen SAVE_AllocGen # endif #endif #ifdef REG_OldHp GLOBAL_REG_DECL(P_,OldHp,REG_OldHp) #else # ifdef REG_ScavBase # define OldHp (ScavBaseReg->rOldHp) # else # define OldHp SAVE_OldHp # endif #endif #if defined(__STG_GCC_REGS__) /* Keep -Wmissing-prototypes from complaining */ void SAVE_REGS PROTO((RegisterTable *dump)); void RESTORE_REGS PROTO((RegisterTable *dump)); extern STG_INLINE void SAVE_REGS(dump) RegisterTable *dump; { #ifdef REG_ScavBase dump->rScavBase = (P_) ScavBaseReg; /* save whatever is in it */ ScavBaseReg = dump; /* set it correctly */ #endif #ifdef REG_Scav dump->rScav = Scav; #endif #ifdef REG_ToHp dump->rToHp = ToHp; #endif #ifdef REG_OldGen dump->rOldGen = OldGen; #endif #ifdef REG_AllocGen dump->rAllocGen = AllocGen; #endif #ifdef REG_OldHp dump->rOldHp = OldHp; #endif } extern STG_INLINE void RESTORE_REGS(dump) RegisterTable *dump; { #ifdef REG_Scav Scav = dump->rScav; #endif #ifdef REG_ToHp ToHp = dump->rToHp; #endif #ifdef REG_OldGen OldGen = dump->rOldGen; #endif #ifdef REG_AllocGen AllocGen = dump->rAllocGen; #endif #ifdef REG_OldHp OldHp = dump->rOldHp; #endif #ifdef REG_ScavBase ScavBaseReg = (RegisterTable *) dump->rScavBase; /* restore to whatever it was */ #endif } #else #define SAVE_REGS(dump) #define RESTORE_REGS(dump) #endif \end{code} %************************************************************************ %* * \subsection[main-mapping-StgRegs]{The main register mapping (Haskell threaded world)} %* * %************************************************************************ \begin{code} #else /* For simplicity, the default is MAIN_REG_MAP (this one) */ \end{code} Main register-mapping summary: (1)~Specific architecture's details are given later. (2)~Entries marked \tr{!} are caller-saves registers that {\em must be saved} across ccalls; those marked \tr{@} are caller-saves registers that need {\em not} be saved; those marked \tr{#} are caller-saves registers that need to be restored, but don't need to be saved; the rest are callee-save registers (the best kind). IF YOU CHANGE THIS TABLE, YOU MAY NEED TO CHANGE CallWrapper.s (or equiv) and [who knows?] maybe something else. Check the documentation in the porter's part of the installation guide. \begin{verbatim} sparc m68k alpha mipseX hppa iX86 powerpc ----- ---- ----- ------ ---- ---- ------- BaseReg# a5 ebx StkOReg (CONCURRENT) R1/Node l1 d7 $1! $9! %r11 R2 l2 d6 $2! $10! %r12 R3 l3 d5 $3! $11! %r13 R4 l4 $4! $12! %r14 R5 l5 $5! $13! %r15 R6 l6 $6! $14! %r16 R7 l7 $7! $15! %r17 R8 $8! $24! %r18 TagReg@ FltReg1 f2! fp2 $f1 $f20 %fr12 FltReg2 f3! fp3 $f2 $f22 %fr12R FltReg3 f4! fp4 $f3 $f24 %fr13 FltReg4 f5! fp5 $f4 $f26 %fr13R DblReg1 f6! fp6 $f5 $f28 %fr20 * SEE NOTES! DblReg2 f8! fp7 $f6 $f30 %fr20 * SEE NOTES! SpA i0 a3 $9 $16 %r4 SuA i1 d3 $10 $17 %r5 SpB i2 a4 $11 $18 %r6 SuB i3 d4 $12 $19 %r7 Hp i4 a2 $13 $20 %r8 HpLim i5 $14 $21 %r9 RetReg l0 $15 $22 %r10 Liveness (CONCURRENT) StdUpdRetVec# StkStub# i7 $23 \end{verbatim} Notes: \begin{enumerate} \item Registers not mentioned in the summary table end up in the default (a memory location in @MainRegTable@). \item @BaseReg@ is in a machine register if anything is (well, unless everything is!) It points to a block of memory in which the things which don't end up in machine registers live. \item Exceptions to previous point: If the following labels are in machine registers, then the corresponding register name refers to what's in its register; otherwise, it refers to the label: \begin{verbatim} StdUpdRetVecReg vtbl_StdUpdFrame StkStubReg STK_STUB_closure \end{verbatim} Also, if TagReg is not in a machine register, its name refers to @INFO_TAG(InfoPtr)@, the tag field from the info table pointed to by register R2 (InfoPtr). \end{enumerate} Next, we have the code to declare the various global registers. Those STG registers which don't actually live in machine registers are defined as macros which refer to the registers as fixed offsets into the register table. Note that the register table will contain blank spots for the STG registers that reside in machine registers. Not to worry; these blank spots will be filled in whenever the register context is saved, so the space does not go to waste. \begin{code} #define Node (R1.p) #define InfoPtr (R2.d) /* these are if we get stuck using the reg-tbl "register" (no machine reg avail) */ #define RTBL_Dbl1 (BaseReg->rDbl[0]) #define RTBL_Dbl2 (BaseReg->rDbl[1]) #define RTBL_Flt1 (BaseReg->rFlt[0]) #define RTBL_Flt2 (BaseReg->rFlt[1]) #define RTBL_Flt3 (BaseReg->rFlt[2]) #define RTBL_Flt4 (BaseReg->rFlt[3]) #define RTBL_R1 (BaseReg->rR[0]) #define RTBL_R2 (BaseReg->rR[1]) #define RTBL_R3 (BaseReg->rR[2]) #define RTBL_R4 (BaseReg->rR[3]) #define RTBL_R5 (BaseReg->rR[4]) #define RTBL_R6 (BaseReg->rR[5]) #define RTBL_R7 (BaseReg->rR[6]) #define RTBL_R8 (BaseReg->rR[7]) #define RTBL_SpA (BaseReg->rSpA) #define RTBL_SuA (BaseReg->rSuA) #define RTBL_SpB (BaseReg->rSpB) #define RTBL_SuB (BaseReg->rSuB) #define RTBL_Hp (BaseReg->rHp) #define RTBL_HpLim (BaseReg->rHpLim) #define RTBL_Tag (BaseReg->rTag) #define RTBL_Ret (BaseReg->rRet) #define RTBL_StkO (BaseReg->rStkO) #define RTBL_Liveness (BaseReg->rLiveness) #ifdef REG_Base GLOBAL_REG_DECL(STGRegisterTable *,BaseReg,REG_Base) #else #ifdef CONCURRENT #define BaseReg CurrentRegTable #else #define BaseReg (&MainRegTable) #endif /* CONCURRENT */ #endif /* REG_Base */ #ifdef REG_StkO GLOBAL_REG_DECL(P_,StkOReg,REG_StkO) #else #define StkOReg RTBL_StkO #endif #ifndef __STG_REGS_AVAIL__ /* driver ensures it is 2 or more */ # define __STG_REGS_AVAIL__ 8 /* R1 to R8 */ /* this would only be non-8 if doing weird experiments (WDP 95/11) */ /* or it might be set lower for a particular arch... */ #endif /* R1 is used for Node */ #ifdef REG_R1 GLOBAL_REG_DECL(StgUnion,R1,REG_R1) #else #define R1 RTBL_R1 #endif /* R2 is used for InfoPtr */ #ifdef REG_R2 GLOBAL_REG_DECL(StgUnion,R2,REG_R2) #else #define R2 RTBL_R2 #endif #ifdef REG_R3 GLOBAL_REG_DECL(StgUnion,R3,REG_R3) #else # define R3 RTBL_R3 #endif #ifdef REG_R4 GLOBAL_REG_DECL(StgUnion,R4,REG_R4) #else # define R4 RTBL_R4 #endif #ifdef REG_R5 GLOBAL_REG_DECL(StgUnion,R5,REG_R5) #else # define R5 RTBL_R5 #endif #ifdef REG_R6 GLOBAL_REG_DECL(StgUnion,R6,REG_R6) #else # define R6 RTBL_R6 #endif #ifdef REG_R7 GLOBAL_REG_DECL(StgUnion,R7,REG_R7) #else # define R7 RTBL_R7 #endif #ifdef REG_R8 GLOBAL_REG_DECL(StgUnion,R8,REG_R8) #else # define R8 RTBL_R8 #endif #ifdef REG_Flt1 GLOBAL_REG_DECL(StgFloat,FltReg1,REG_Flt1) #else #define FltReg1 RTBL_Flt1 #endif #ifdef REG_Flt2 GLOBAL_REG_DECL(StgFloat,FltReg2,REG_Flt2) #else #define FltReg2 RTBL_Flt2 #endif #ifdef REG_Flt3 GLOBAL_REG_DECL(StgFloat,FltReg3,REG_Flt3) #else #define FltReg3 RTBL_Flt3 #endif #ifdef REG_Flt4 GLOBAL_REG_DECL(StgFloat,FltReg4,REG_Flt4) #else #define FltReg4 RTBL_Flt4 #endif #ifdef REG_Dbl1 GLOBAL_REG_DECL(StgDouble,DblReg1,REG_Dbl1) #else #define DblReg1 RTBL_Dbl1 #endif #ifdef REG_Dbl2 GLOBAL_REG_DECL(StgDouble,DblReg2,REG_Dbl2) #else #define DblReg2 RTBL_Dbl2 #endif #ifdef REG_Tag GLOBAL_REG_DECL(I_,TagReg,REG_Tag) #define SET_TAG(tag) TagReg = tag #else #define TagReg INFO_TAG(InfoPtr) #define SET_TAG(tag) /* nothing */ #endif #ifdef REG_Ret GLOBAL_REG_DECL(StgRetAddr,RetReg,REG_Ret) #else #define RetReg RTBL_Ret #endif #ifdef REG_SpA GLOBAL_REG_DECL(PP_,SpA,REG_SpA) #else #define SpA RTBL_SpA #endif #ifdef REG_SuA GLOBAL_REG_DECL(PP_,SuA,REG_SuA) #else #define SuA RTBL_SuA #endif #ifdef REG_SpB GLOBAL_REG_DECL(P_,SpB,REG_SpB) #else #define SpB RTBL_SpB #endif #ifdef REG_SuB GLOBAL_REG_DECL(P_,SuB,REG_SuB) #else #define SuB RTBL_SuB #endif #ifdef REG_Hp GLOBAL_REG_DECL(P_,Hp,REG_Hp) #else #define Hp RTBL_Hp #endif #ifdef REG_HpLim GLOBAL_REG_DECL(P_,HpLim,REG_HpLim) #else #define HpLim RTBL_HpLim #endif #ifdef REG_Liveness GLOBAL_REG_DECL(I_,LivenessReg,REG_Liveness) #else #define LivenessReg RTBL_Liveness #endif #ifdef REG_StdUpdRetVec GLOBAL_REG_DECL(D_,StdUpdRetVecReg,REG_StdUpdRetVec) #else #define StdUpdRetVecReg vtbl_StdUpdFrame #endif #ifdef REG_StkStub GLOBAL_REG_DECL(P_,StkStubReg,REG_StkStub) #else #define StkStubReg STK_STUB_closure #endif #ifdef CALLER_SAVES_StkO #define CALLER_SAVE_StkO SAVE_StkO = StkOReg; #define CALLER_RESTORE_StkO StkOReg = SAVE_StkO; #else #define CALLER_SAVE_StkO /* nothing */ #define CALLER_RESTORE_StkO /* nothing */ #endif #ifdef CALLER_SAVES_R1 #define CALLER_SAVE_R1 SAVE_R1 = R1; #define CALLER_RESTORE_R1 R1 = SAVE_R1; #else #define CALLER_SAVE_R1 /* nothing */ #define CALLER_RESTORE_R1 /* nothing */ #endif #ifdef CALLER_SAVES_R2 #define CALLER_SAVE_R2 SAVE_R2 = R2; #define CALLER_RESTORE_R2 R2 = SAVE_R2; #else #define CALLER_SAVE_R2 /* nothing */ #define CALLER_RESTORE_R2 /* nothing */ #endif #ifdef CALLER_SAVES_R3 #define CALLER_SAVE_R3 SAVE_R3 = R3; #define CALLER_RESTORE_R3 R3 = SAVE_R3; #else #define CALLER_SAVE_R3 /* nothing */ #define CALLER_RESTORE_R3 /* nothing */ #endif #ifdef CALLER_SAVES_R4 #define CALLER_SAVE_R4 SAVE_R4 = R4; #define CALLER_RESTORE_R4 R4 = SAVE_R4; #else #define CALLER_SAVE_R4 /* nothing */ #define CALLER_RESTORE_R4 /* nothing */ #endif #ifdef CALLER_SAVES_R5 #define CALLER_SAVE_R5 SAVE_R5 = R5; #define CALLER_RESTORE_R5 R5 = SAVE_R5; #else #define CALLER_SAVE_R5 /* nothing */ #define CALLER_RESTORE_R5 /* nothing */ #endif #ifdef CALLER_SAVES_R6 #define CALLER_SAVE_R6 SAVE_R6 = R6; #define CALLER_RESTORE_R6 R6 = SAVE_R6; #else #define CALLER_SAVE_R6 /* nothing */ #define CALLER_RESTORE_R6 /* nothing */ #endif #ifdef CALLER_SAVES_R7 #define CALLER_SAVE_R7 SAVE_R7 = R7; #define CALLER_RESTORE_R7 R7 = SAVE_R7; #else #define CALLER_SAVE_R7 /* nothing */ #define CALLER_RESTORE_R7 /* nothing */ #endif #ifdef CALLER_SAVES_R8 #define CALLER_SAVE_R8 SAVE_R8 = R8; #define CALLER_RESTORE_R8 R8 = SAVE_R8; #else #define CALLER_SAVE_R8 /* nothing */ #define CALLER_RESTORE_R8 /* nothing */ #endif #ifdef CALLER_SAVES_FltReg1 #define CALLER_SAVE_FltReg1 SAVE_Flt1 = FltReg1; #define CALLER_RESTORE_FltReg1 FltReg1 = SAVE_Flt1; #else #define CALLER_SAVE_FltReg1 /* nothing */ #define CALLER_RESTORE_FltReg1 /* nothing */ #endif #ifdef CALLER_SAVES_FltReg2 #define CALLER_SAVE_FltReg2 SAVE_Flt2 = FltReg2; #define CALLER_RESTORE_FltReg2 FltReg2 = SAVE_Flt2; #else #define CALLER_SAVE_FltReg2 /* nothing */ #define CALLER_RESTORE_FltReg2 /* nothing */ #endif #ifdef CALLER_SAVES_FltReg3 #define CALLER_SAVE_FltReg3 SAVE_Flt3 = FltReg3; #define CALLER_RESTORE_FltReg3 FltReg3 = SAVE_Flt3; #else #define CALLER_SAVE_FltReg3 /* nothing */ #define CALLER_RESTORE_FltReg3 /* nothing */ #endif #ifdef CALLER_SAVES_FltReg4 #define CALLER_SAVE_FltReg4 SAVE_Flt4 = FltReg4; #define CALLER_RESTORE_FltReg4 FltReg4 = SAVE_Flt4; #else #define CALLER_SAVE_FltReg4 /* nothing */ #define CALLER_RESTORE_FltReg4 /* nothing */ #endif #ifdef CALLER_SAVES_DblReg1 #define CALLER_SAVE_DblReg1 SAVE_Dbl1 = DblReg1; #define CALLER_RESTORE_DblReg1 DblReg1 = SAVE_Dbl1; #else #define CALLER_SAVE_DblReg1 /* nothing */ #define CALLER_RESTORE_DblReg1 /* nothing */ #endif #ifdef CALLER_SAVES_DblReg2 #define CALLER_SAVE_DblReg2 SAVE_Dbl2 = DblReg2; #define CALLER_RESTORE_DblReg2 DblReg2 = SAVE_Dbl2; #else #define CALLER_SAVE_DblReg2 /* nothing */ #define CALLER_RESTORE_DblReg2 /* nothing */ #endif #ifdef CALLER_SAVES_Tag #define CALLER_SAVE_Tag SAVE_Tag = TagReg; #define CALLER_RESTORE_Tag TagReg = SAVE_Tag; #else #define CALLER_SAVE_Tag /* nothing */ #define CALLER_RESTORE_Tag /* nothing */ #endif #ifdef CALLER_SAVES_Ret #define CALLER_SAVE_Ret SAVE_Ret = RetReg; #define CALLER_RESTORE_Ret RetReg = SAVE_Ret; #else #define CALLER_SAVE_Ret /* nothing */ #define CALLER_RESTORE_Ret /* nothing */ #endif #ifdef CALLER_SAVES_SpA #define CALLER_SAVE_SpA SAVE_SpA = SpA; #define CALLER_RESTORE_SpA SpA = SAVE_SpA; #else #define CALLER_SAVE_SpA /* nothing */ #define CALLER_RESTORE_SpA /* nothing */ #endif #ifdef CALLER_SAVES_SuA #define CALLER_SAVE_SuA SAVE_SuA = SuA; #define CALLER_RESTORE_SuA SuA = SAVE_SuA; #else #define CALLER_SAVE_SuA /* nothing */ #define CALLER_RESTORE_SuA /* nothing */ #endif #ifdef CALLER_SAVES_SpB #define CALLER_SAVE_SpB SAVE_SpB = SpB; #define CALLER_RESTORE_SpB SpB = SAVE_SpB; #else #define CALLER_SAVE_SpB /* nothing */ #define CALLER_RESTORE_SpB /* nothing */ #endif #ifdef CALLER_SAVES_SuB #define CALLER_SAVE_SuB SAVE_SuB = SuB; #define CALLER_RESTORE_SuB SuB = SAVE_SuB; #else #define CALLER_SAVE_SuB /* nothing */ #define CALLER_RESTORE_SuB /* nothing */ #endif #ifdef CALLER_SAVES_Hp #define CALLER_SAVE_Hp SAVE_Hp = Hp; #define CALLER_RESTORE_Hp Hp = SAVE_Hp; #else #define CALLER_SAVE_Hp /* nothing */ #define CALLER_RESTORE_Hp /* nothing */ #endif #ifdef CALLER_SAVES_HpLim #define CALLER_SAVE_HpLim SAVE_HpLim = HpLim; #define CALLER_RESTORE_HpLim HpLim = SAVE_HpLim; #else #define CALLER_SAVE_HpLim /* nothing */ #define CALLER_RESTORE_HpLim /* nothing */ #endif #ifdef CALLER_SAVES_Liveness #define CALLER_SAVE_Liveness SAVE_Liveness = LivenessReg; #define CALLER_RESTORE_Liveness LivenessReg = SAVE_Liveness; #else #define CALLER_SAVE_Liveness /* nothing */ #define CALLER_RESTORE_Liveness /* nothing */ #endif #ifdef CALLER_SAVES_Base #ifndef CONCURRENT #define CALLER_SAVE_Base /* nothing, ever (it holds a fixed value) */ #define CALLER_RESTORE_Base BaseReg = &MainRegTable; #else #define CALLER_SAVE_Base /* nothing */ #define CALLER_RESTORE_Base BaseReg = CurrentRegTable; #endif #else #define CALLER_SAVE_Base /* nothing */ #define CALLER_RESTORE_Base /* nothing */ #endif #ifdef CALLER_SAVES_StdUpdRetVec #define CALLER_RESTORE_StdUpdRetVec StdUpdRetVecReg = vtbl_StdUpdFrame; #else #define CALLER_RESTORE_StdUpdRetVec /* nothing */ #endif #ifdef CALLER_SAVES_StkStub #define CALLER_RESTORE_StkStub StdUpdRetVecReg = STK_STUB_closure; #else #define CALLER_RESTORE_StkStub /* nothing */ #endif \end{code} Concluding \tr{#endifs} and multi-slurp protection: \begin{code} #endif /* SCAV_REG_MAP */ #endif /* SCAN_REG_MAP */ #endif /* MARK_REG_MAP */ #endif /* NULL_REG_MAP */ #endif /* STGREGS_H */ \end{code}