% % (c) The GRASP Project, Glasgow University, 1994 % \section[MachRegs-decls]{Architecture-specific register mappings} NB: THIS FILE IS INCLUDED IN HASKELL SOURCE! \begin{code} #ifndef MACHREGS_H #define MACHREGS_H #if defined(__STG_GCC_REGS__) || defined(COMPILING_GHC) #include "StgMachDeps.h" \end{code} %************************************************************************ %* * \subsection[register-tricks]{Things to watch out for} %* * %************************************************************************ Here we collect random pieces of lore about register mapping. It is a delicate topic. The best place to get a blow-by-blow account of the register story for a particular machine architecture is in the ``main mapping'' section (\sectionref{main-mapping-StgRegs}). %************************************************************************ %* * \subsubsection{Using callee-saves registers} %* * %************************************************************************ Mostly we map variables to callee-saves registers. These are best---the only thing you have to worry about is that whoever starts up a wadge of code that uses them does, in fact, save them! (i.e., observes the callee-save convention). To see what GCC thinks is a callee-save register: Look at the \tr{FIXED_REGISTERS} and \tr{CALL_USED_REGISTERS} tables for an architecture. A register that is marked as \tr{0} in both tables is callee-save. Of course, whatever you do, it isn't wise to grab {\em all} available registers, because then GCC has nothing to use for temporaries, etc. %************************************************************************ %* * \subsubsection{Using caller-saves registers} %* * %************************************************************************ Using callee-saves registers only isn't really good enough. For example, on a SPARC, {\em none} of the floating-point registers are callee-save, so it's caller-save registers or terrible performance... Also, modern RISC architectures normally offer about eight callee-save registers; we'd really rather steal more machine registers. We need to be extra careful with stolen caller-save registers. We don't want to save them {\em all} across every call to a C function (specifically @ccall@s). Here is table of what STG registers {\em must} be saved across @ccalls@ if they are held in caller-save registers: \begin{verbatim} SpA, SuA, must be saved *every time* if SpB, SuB, Hp, HpLim in a caller-save reg (to be avoided) TagReg Usually won't be in a register, unless we have a register glut. In any event, it needn't be saved; it isn't live at ccalls. CLAIM: it isn't alive during other PrimOps either! R1 (Node), R2 ... R8 must be saved if in a caller-save reg *and* FltReg1 .. n its value survives over the ccall [rare] (the DblReg1 .. n code-generator knows this...) BaseReg, better choices for caller-saves regs than the StkStub,... other system regs, since they only need to be restored, and never saved. (For PAR, BaseReg is not fixed, and must be saved.) \end{verbatim} So: the compiler emits suitable \tr{CALLER_SAVE_} macros before a ccall, and corresponding \tr{...RESTORE...}s afterwards. It {\em always} emits them for @BaseReg@ and friends; it emits them only for the known-to-be-live @R1@, ..., \tr{DoubleRegN} and friends. Notice that PrimOps like arbitrary-precision arithmetic can trigger GC, so everything gets saved on the stack safely. Special care needs to be taken for ``invisible'' calls to C functions. In particular, we freely generate calls to \tr{{PK,UNPK}_{FLT,DBL}}, to construct and deconstruct floats and doubles. These {\em must} be inlined by @gcc@, because otherwise they trash floating point registers. So you have to compile with at least @gcc -O@ if you want registerisation. Morals: We try {\em very hard} to keep @BaseReg@ and friends in callee-save registers. If we have callee-save registers available for @R1@ (@Node@), etc., we use them on the more-heavily-used ``low-numbered'' registers. As the likelihood of having to do a SAVE/RESTORE for @R6@ (for example) is near zero, it's OK to assign it a caller-save register. On machines with register-based calling conventions, never try to steal argument or return registers, for two reasons: (1) it breaks the callWrapper approach (saves and restores have to be done inline), and (2) it means that you have to be extremely careful about setting up arguments for calls (and may in fact have to introduce temporaries to handle simultaneous assignments to/from the argument registers). %************************************************************************ %* * \subsubsection[mapping-alpha]{The DEC Alpha register mapping} %* * %************************************************************************ Alpha registers \tr{$9}--\tr{$14} are our ``prize'' callee-save registers. \tr{$15} is the frame pointer, and \tr{$16}--\tr{$21} are argument registers. (These are off-limits.) We can steal some of the \tr{$22}-and-up caller-save registers provided we do the appropriate save/restore stuff. \tr{$f2}--\tr{$f9} are some callee-save floating-point registers. We cannot use \tr{$23} (aka t9), \tr{$24} (aka t10), \tr{$25} (aka t11), \tr{$27} (aka pv), or \tr{$28} (aka at), because they are occasionally required by the assembler to handle non-primitive instructions (e.g. ldb, remq). Sigh! Cheat sheet for GDB: \begin{verbatim} GDB here Main map === ==== ======== t0 $1 R1 t1 $2 R2 t2 $3 R3 t3 $4 R4 t4 $5 R5 t5 $6 R6 t6 $7 R7 t7 $8 R8 s0 $9 SpA s1 $10 SuA s2 $11 SpB s3 $12 SuB s4 $13 Hp s5 $14 HpLim fp $15 RetReg t8 $22 NCG_reserved t12 $27 NCG_reserved \end{verbatim} \begin{code} #if defined(alpha_TARGET_ARCH) # define REG(x) __asm__("$" #x) # if defined(MARK_REG_MAP) # define REG_Mark 9 # define REG_MStack 10 # define REG_MRoot 11 # define REG_BitArray 12 # define REG_HeapBase 13 # define REG_HeapLim 14 # else # if defined(SCAN_REG_MAP) # define REG_Scan 9 # define REG_New 10 # define REG_LinkLim 11 # else # if defined(SCAV_REG_MAP) # define REG_Scav 9 # define REG_ToHp 10 # if defined(GCap) || defined(GCgn) # define REG_OldGen 11 # endif /* GCap || GCgn */ # else /* default: MAIN_REG_MAP */ /* callee saves */ # define CALLEE_SAVES_FltReg1 # define CALLEE_SAVES_FltReg2 # define CALLEE_SAVES_FltReg3 # define CALLEE_SAVES_FltReg4 # define CALLEE_SAVES_DblReg1 # define CALLEE_SAVES_DblReg2 # define CALLEE_SAVES_SpA # define CALLEE_SAVES_SuA # define CALLEE_SAVES_SpB # define CALLEE_SAVES_SuB # define CALLEE_SAVES_Hp # define CALLEE_SAVES_HpLim # define CALLEE_SAVES_Ret /* caller saves */ # define CALLER_SAVES_R1 # define CALLER_SAVES_R2 # define CALLER_SAVES_R3 # define CALLER_SAVES_R4 # define CALLER_SAVES_R5 # define CALLER_SAVES_R6 # define CALLER_SAVES_R7 # define CALLER_SAVES_R8 # define CALLER_SAVES_USER # define REG_R1 1 # define REG_R2 2 # define REG_R3 3 # define REG_R4 4 # define REG_R5 5 # define REG_R6 6 # define REG_R7 7 # define REG_R8 8 # define REG_Flt1 f2 # define REG_Flt2 f3 # define REG_Flt3 f4 # define REG_Flt4 f5 # define REG_Dbl1 f6 # define REG_Dbl2 f7 # define REG_SpA 9 # define REG_SuA 10 # define REG_SpB 11 # define REG_SuB 12 # define REG_Hp 13 # define REG_HpLim 14 # define REG_Ret 15 # define NCG_Reserved_I1 22 # define NCG_Reserved_I2 27 # define NCG_Reserved_F1 f29 # define NCG_Reserved_F2 f30 # endif /* !SCAV_REG_MAP */ # endif /* !SCAN_REG_MAP */ # endif /* !MARK_REG_MAP */ #endif /* alpha */ \end{code} %************************************************************************ %* * \subsubsection[mapping-hpux]{The HP-PA register mapping} %* * %************************************************************************ We cater for HP-PA 1.1. \tr{%r0}--\tr{%r1} are special. \tr{%r2} is the return pointer. \tr{%r3} is the frame pointer. \tr{%r4}--\tr{%r18} are callee-save registers. \tr{%r19} is a linkage table register for HPUX 8.0 shared libraries. \tr{%r20}--\tr{%r22} are caller-save registers. \tr{%r23}--\tr{%r26} are parameter registers. \tr{%r27} is a global data pointer. \tr{%r28}--\tr{%r29} are temporaries. \tr{%r30} is the stack pointer. \tr{%r31} is a temporary. \tr{%fr12}--\tr{%fr15} are some callee-save floating-point registers. \tr{%fr8}--\tr{%fr11} are some available caller-save fl-pt registers. \begin{code} #if hppa1_1_TARGET_ARCH #define REG(x) __asm__("%" #x) #if defined(MARK_REG_MAP) #define REG_Mark r4 #define REG_MStack r5 #define REG_MRoot r6 #define REG_BitArray r7 #define REG_HeapBase r8 #define REG_HeapLim r9 #else #if defined(SCAN_REG_MAP) #define REG_Scan r4 #define REG_New r5 #define REG_LinkLim r6 #else #if defined(SCAV_REG_MAP) #define REG_Scav r4 #define REG_ToHp r5 #if defined(GCap) || defined(GCgn) #define REG_OldGen r6 #endif /* GCap || GCgn */ #else /* default: MAIN_REG_MAP */ /* callee saves */ #define CALLEE_SAVES_FltReg1 #define CALLEE_SAVES_FltReg2 #define CALLEE_SAVES_FltReg3 #define CALLEE_SAVES_FltReg4 #define CALLEE_SAVES_DblReg1 #define CALLEE_SAVES_DblReg2 #define CALLEE_SAVES_SpA #define CALLEE_SAVES_SuA #define CALLEE_SAVES_SpB #define CALLEE_SAVES_SuB #define CALLEE_SAVES_Hp #define CALLEE_SAVES_HpLim #define CALLEE_SAVES_Ret #define CALLEE_SAVES_R1 #define CALLEE_SAVES_R2 #define CALLEE_SAVES_R3 #define CALLEE_SAVES_R4 #define CALLEE_SAVES_R5 #define CALLEE_SAVES_R6 #define CALLEE_SAVES_R7 #define CALLEE_SAVES_R8 /* caller saves -- none */ #define REG_R1 r11 #define REG_R2 r12 #define REG_R3 r13 #define REG_R4 r14 #define REG_R5 r15 #define REG_R6 r16 #define REG_R7 r17 #define REG_R8 r18 #define REG_Flt1 fr12 #define REG_Flt2 fr12R #define REG_Flt3 fr13 #define REG_Flt4 fr13R #define REG_Dbl1 fr20 /* L & R */ #define REG_Dbl2 fr21 /* L & R */ #define REG_SpA r4 #define REG_SuA r5 #define REG_SpB r6 #define REG_SuB r7 #define REG_Hp r8 #define REG_HpLim r9 #define REG_Ret r10 /* #define REG_StkStub r2 */ #define NCG_Reserved_I1 r28 #define NCG_Reserved_I2 r29 #define NCG_Reserved_F1 fr8 #define NCG_Reserved_F2 fr8R #define NCG_Reserved_D1 fr10 #define NCG_Reserved_D2 fr11 #endif /* SCAV_REG_MAP */ #endif /* SCAN_REG_MAP */ #endif /* MARK_REG_MAP */ #endif /* hppa */ \end{code} %************************************************************************ %* * \subsubsection[mapping-iX86]{The Intel iX86 register mapping} %* * %************************************************************************ Ok, we've only got 6 general purpose registers, a frame pointer and a stack pointer. \tr{%eax} and \tr{%edx} are return values from C functions, hence they get trashed across ccalls and are caller saves. \tr{%ebx}, \tr{%esi}, \tr{%edi}, \tr{%ebp} are all callee-saves. We only steal \tr{%ebx} for base registers. SIGH. SimonM also took \tr{%esi} for SpA and \tr{%edi} for SpB. Maybe later. \begin{code} #if i386_TARGET_ARCH #define REG(x) __asm__("%" #x) #if defined(MARK_REG_MAP) #define REG_MarkBase ebx #define REG_Mark ebp #define REG_MStack esi #define REG_MRoot edi #else #if defined(SCAN_REG_MAP) #define REG_Scan ebx #define REG_New ebp #define REG_LinkLim esi #else #if defined(SCAV_REG_MAP) #define REG_Scav ebx #define REG_ToHp ebp #if defined(GCap) || defined(GCgn) #define REG_OldGen esi #endif /* GCap || GCgn */ #else /* default: MAIN_REG_MAP */ /* callee saves */ #define CALLEE_SAVES_Base #define CALLEE_SAVES_SpB /* caller saves -- none */ /* After trying to steal 4 regs, ... crash: works again if: - give back esi - give back edi - give back edi & ebp does not work if - give back ebp */ /* SpB and R1 are the two heaviest hitters, followed by SpA */ #define REG_Base ebx #define REG_SpB ebp #if STOLEN_X86_REGS >= 3 # define REG_R1 esi # define CALLEE_SAVES_R1 #endif #if STOLEN_X86_REGS >= 4 # define REG_SpA edi # define CALLEE_SAVES_SpA #endif /* the mangler will put Hp in %esp!!! */ #if defined(MANGLING_X86_SP) && MANGLING_X86_SP == 0 Oops! You should not be here if not mangling %esp! #endif #if STOLEN_X86_REGS >= 5 # define REG_R2 ecx # define CALLER_SAVES_R2 #endif #endif /* SCAV_REG_MAP */ #endif /* SCAN_REG_MAP */ #endif /* MARK_REG_MAP */ #endif /* iX86 */ \end{code} %************************************************************************ %* * \subsubsection[mapping-m68k]{The Motorola 680x0 register mapping} %* * %************************************************************************ A Sun3 (mc680x0) has eight address registers, \tr{a0} to \tr{a7}, and eight data registers, \tr{d0} to \tr{d7}. Address operations have to be done through address registers; data registers are used for comparison values and data. Here's the register-usage picture for m68k boxes with GCC. \begin{tabular}{ll} a0 & used directly by GCC \\ a1 & used directly by GCC \\ \\ a2..a5 & callee-saved: available for STG registers \\ & (a5 may be special, ``global'' register for PIC?) \\ \\ a6 & C-stack frame pointer \\ a7 & C-stack pointer \\ \\ d0 & used directly by GCC \\ d1 & used directly by GCC \\ d2 & really needed for local optimisation by GCC \\ \\ d3..d7 & callee-saved: available for STG registers \\ fp0 & call-clobbered \\ fp1 & call-clobbered \\ fp2..fp7 & callee-saved: available for STG registers \end{tabular} \begin{code} #if m68k_TARGET_ARCH #define REG(x) __asm__(#x) #if defined(FLUSH_REG_MAP) #define REG_FlushP a2 #define REG_FStack a3 #define REG_FlushTemp a4 #else #if defined(MARK_REG_MAP) #define REG_Mark a2 #define REG_MStack a3 #define REG_MRoot a4 #define REG_BitArray a5 #define REG_HeapBase d3 #define REG_HeapLim d4 #else #if defined(SCAN_REG_MAP) #define REG_Scan a2 #define REG_New a3 #define REG_LinkLim a4 #else #if defined(SCAV_REG_MAP) #define REG_Scav a2 #define REG_ToHp a3 #if defined(GCap) #define REG_OldGen a4 #else #if defined(GCgn) #define REG_OldGen a4 #define REG_AllocGen a5 #define REG_OldHp d3 #endif /* GCap */ #endif /* GCgn */ #else /* default: MAIN_REG_MAP */ /* callee saves */ #define CALLEE_SAVES_FltReg1 #define CALLEE_SAVES_DblReg1 #if !defined(CONCURRENT) # define CALLEE_SAVES_FltReg2 # define CALLEE_SAVES_FltReg3 # define CALLEE_SAVES_FltReg4 # define CALLEE_SAVES_DblReg2 #endif #define CALLEE_SAVES_Base #define CALLEE_SAVES_SpB #define CALLEE_SAVES_SpA #define CALLEE_SAVES_Hp #define CALLEE_SAVES_SuA #define CALLEE_SAVES_SuB #define CALLEE_SAVES_R1 #define CALLEE_SAVES_R2 #define CALLEE_SAVES_Ret /* caller saves -- none */ #define REG_Base a2 #define REG_SpB a3 #define REG_SpA a4 #define REG_Hp d3 #define REG_SuA d4 #define REG_SuB d5 #define REG_R1 a5 #define REG_R2 d6 #define REG_Ret d7 #define REG_Flt1 fp2 #if !defined(CONCURRENT) /* The extra float registers are not worth the tradeoff in context-switch time for most programs (for now, at least). */ #define REG_Flt2 fp3 #define REG_Flt3 fp4 #define REG_Flt4 fp5 #endif #define REG_Dbl1 fp6 /* The extra double registers are not worth the tradeoff in context-switch time for most programs (for now, at least). */ #if !defined(CONCURRENT) #define REG_Dbl2 fp7 #endif #endif /* SCAV_REG_MAP */ #endif /* SCAN_REG_MAP */ #endif /* MARK_REG_MAP */ #endif /* FLUSH_REG_MAP */ #endif /* m68k */ \end{code} %************************************************************************ %* * \subsubsection[mapping-mipsel]{The DECstation (MIPS) register mapping} %* * %************************************************************************ Here's at least some simple stuff about registers on a MIPS. \tr{s0}--\tr{s7} are callee-save integer registers; they are our ``prize'' stolen registers. There is also a wad of callee-save floating-point registers, \tr{$f20}--\tr{$f31}; we'll use some of those. \tr{t0}--\tr{t9} are caller-save (``temporary?'') integer registers. We can steal some, but we might have to save/restore around ccalls. \begin{code} #if mipsel_TARGET_ARCH || mipseb_TARGET_ARCH #define REG(x) __asm__("$" #x) #if defined(MARK_REG_MAP) #define REG_Mark 16 #define REG_MStack 17 #define REG_MRoot 18 #define REG_BitArray 19 #define REG_HeapBase 20 #define REG_HeapLim 21 #else #if defined(SCAN_REG_MAP) #define REG_Scan 16 #define REG_New 17 #define REG_LinkLim 18 #else #if defined(SCAV_REG_MAP) #define REG_Scav 16 #define REG_ToHp 17 #if defined(GCap) || defined(GCgn) #define REG_OldGen 18 #endif /* GCap || GCgn */ #else /* default: MAIN_REG_MAP */ /* callee saves */ #define CALLEE_SAVES_FltReg1 #define CALLEE_SAVES_FltReg2 #define CALLEE_SAVES_FltReg3 #define CALLEE_SAVES_FltReg4 #define CALLEE_SAVES_DblReg1 #define CALLEE_SAVES_DblReg2 #define CALLEE_SAVES_SpA #define CALLEE_SAVES_SuA #define CALLEE_SAVES_SpB #define CALLEE_SAVES_SuB #define CALLEE_SAVES_Hp #define CALLEE_SAVES_HpLim #define CALLEE_SAVES_Ret /* caller saves */ #define CALLER_SAVES_R1 #define CALLER_SAVES_R2 #define CALLER_SAVES_R3 #define CALLER_SAVES_R4 #define CALLER_SAVES_R5 #define CALLER_SAVES_R6 #define CALLER_SAVES_R7 #define CALLER_SAVES_R8 #define CALLER_SAVES_USER #define REG_R1 9 #define REG_R2 10 #define REG_R3 11 #define REG_R4 12 #define REG_R5 13 #define REG_R6 14 #define REG_R7 15 #define REG_R8 24 #define REG_Flt1 f20 #define REG_Flt2 f22 #define REG_Flt3 f24 #define REG_Flt4 f26 #define REG_Dbl1 f28 #define REG_Dbl2 f30 #define REG_SpA 16 #define REG_SuA 17 #define REG_SpB 18 #define REG_SuB 19 #define REG_Hp 20 #define REG_HpLim 21 #define REG_Ret 22 #define REG_StkStub 23 #endif /* SCAV_REG_MAP */ #endif /* SCAN_REG_MAP */ #endif /* MARK_REG_MAP */ #endif /* mipse[lb] */ \end{code} %************************************************************************ %* * \subsubsection[mapping-rs6000]{The IBM RS6000 register mapping} %* * %************************************************************************ \tr{r13}--\tr{r31} are wonderful callee-save registers. \tr{r4}--\tr{r8}, \tr{r10}, and \tr{r11} are caller-save registers. \tr{%fr14}--\tr{%fr31} are callee-save floating-point registers. I think we can do the Whole Business with callee-save registers only! UTTERLY UNTESTED \begin{code} #if rs6000_TARGET_ARCH #define REG_Base ???? #define REG_R1 #define REG_SpA #define REG_SpB #define REG_Hp #define REG_Flt1 #define REG_Flt2 #define REG_Flt3 #define REG_Flt4 #define REG_Dbl1 #define REG_Dbl2 #endif /* rs6000 */ \end{code} %************************************************************************ %* * \subsubsection[mapping-sparc]{The Sun SPARC register mapping} %* * %************************************************************************ The SPARC register (window) story: Remember, within the Haskell Threaded World, we essentially ``shut down'' the register-window mechanism---the window doesn't move at all while in this World. It {\em does} move, of course, if we call out to arbitrary~C... The \tr{%i}, \tr{%l}, and \tr{%o} registers (8 each) are the input, local, and output registers visible in one register window. The 8 \tr{%g} (global) registers are visible all the time. \begin{tabular}{ll} \tr{%o0}..\tr{%o7} & not available; can be zapped by callee \\ & (\tr{%o6} is C-stack ptr; \tr{%o7} hold ret addrs) \\ \tr{%i0}..\tr{%i7} & available (except \tr{%i6} is used as frame ptr) \\ & (and \tr{%i7} tends to have ret-addr-ish things) \\ \tr{%l0}..\tr{%l7} & available \\ \tr{%g0}..\tr{%g4} & not available; prone to stomping by division, etc.\\ \tr{%g5}..\tr{%g7} & not available; reserved for the OS \\ \end{tabular} Note: \tr{%g3} is {\em definitely} clobbered in the builtin divide code (and our save/restore machinery is NOT GOOD ENOUGH for that); discretion being the better part of valor, we also don't take \tr{%g4}. \begin{code} #if sparc_TARGET_ARCH #define REG(x) __asm__("%" #x) #if defined(MARK_REG_MAP) #define REG_Mark i0 #define REG_MStack i1 #define REG_MRoot i2 #define REG_BitArray i3 #define REG_HeapBase i4 #define REG_HeapLim i5 #else #if defined(SCAN_REG_MAP) #define REG_ScanBase g4 #else #if defined(SCAV_REG_MAP) #define REG_ScavBase g4 #else /* default: MAIN_REG_MAP */ /* callee saves (nothing) */ /* caller saves (fp registers and maybe Activity) */ #if defined(DO_SPAT_PROFILING) #define CALLER_SAVES_SYSTEM #define CALLER_SAVES_Activity #endif #define CALLER_SAVES_USER #define CALLER_SAVES_FltReg1 #define CALLER_SAVES_FltReg2 #define CALLER_SAVES_FltReg3 #define CALLER_SAVES_FltReg4 #define CALLER_SAVES_DblReg1 #define CALLER_SAVES_DblReg2 #define REG_R1 l1 #define REG_R2 l2 #define REG_R3 l3 #define REG_R4 l4 #define REG_R5 l5 #define REG_R6 l6 #define REG_R7 l7 #define REG_Flt1 f2 #define REG_Flt2 f3 #define REG_Flt3 f4 #define REG_Flt4 f5 #define REG_Dbl1 f6 #define REG_Dbl2 f8 #if defined(DO_SPAT_PROFILING) #define REG_Activity g5 #endif #define REG_SpA i0 #define REG_SuA i1 #define REG_SpB i2 #define REG_SuB i3 #define REG_Hp i4 #define REG_HpLim i5 #define REG_Ret l0 #define REG_StkStub i7 #define NCG_Reserved_I1 g1 #define NCG_Reserved_I2 g2 #define NCG_Reserved_F1 f14 #define NCG_Reserved_F2 f15 #define NCG_Reserved_D1 f16 #define NCG_Reserved_D2 f18 #endif /* SCAV_REG_MAP */ #endif /* SCAN_REG_MAP */ #endif /* MARK_REG_MAP */ #endif /* sparc */ \end{code} Concluding multi-slurp protection: \begin{code} #endif /* __STG_GCC_REGS__ || COMPILING_GHC */ #endif /* MACHREGS_H */ \end{code}