Next | Prev | Up | Top | Contents | Index

Assembly Language Issues

Since get_gp() is a leaf routine that is linked in the same DSO where it is called, no changes are required to port it to n32. However, you have to recompile it.

Since get_gp() is a leaf routine that is linked in the same DSO where it is called, no changes are required to port it to n32. However, you have to recompile it.

On the other hand, regs() requires a lot of work. The issues that need to be addressed are detailed below.


gp register

As explained throughout this book, the o32 ABI follows the convention that $gp (global pointer register) is caller saved. This means that the global pointer is saved before each function call and restored after each function call returns. This is accomplished by using the .cpload and .cprestore assembler pseudo instructions respectively. Both lines are present in the original version of regs.s.

The n32 ABI, on the other hand, follows the convention that $gp is callee saved. This means that $gp is saved at the beginning of each routine and restored right before that routine itself returns. This is accomplished through the use of .cpsetup, an assembler pseudo instruction.

The recommended way to deal with these various pseudo instructions is to use the macros provided in <sys/asm.h>. The macros below will provide correct use of these pseudo instructions whether compiled for o32 or for n32.


Register Size

Under o32, registers are 32 bits wide. Under n32, they are 64 bits wide. As a result, assembly language routines must be careful in the way they operate on registers. The following macros defined in <sys/asm.h> are useful because they expand to 32-bit instructions under o32 and to 64-bit instructions under n32.


Argument Passing

The get_regs() function in regs.s is called with six arguments. Under o32, the first three are passed in registers a0 through a2. The fourth argument (a double precision parameter) is passed at offset 16 relative to the stack. The fifth and sixth arguments are passed at offsets 24 and 28 relative to the stack, respectively. Under n32, however, all of the arguments are passed in registers. The first three arguments are passed in registers a0 through a2 as they were under o32. The next parameter is passed in register $f15. The last two parameters are passed in registers a4 and a5 respectively. Table 4-1 summarizes where each of the arguments are passed under the two conventions.

Argument Passing
Argumento32 n32
argument1a0a0
argument2a1a1
argument3a2a2
argument4$sp+16$f15
argument5$sp+24a4
argument6$sp+28a5

Note: Under o32, there are no a4 and a5 registers, but under n32 they must be saved on the stack because they are used after calls to an external function. The code fragment that illustrates accessing the arguments under n32 is shown below:

        mov.d   $f4,$f15            # 5th argument in 5th fp
                                    # arg. register
        l.d     $f6,0(a4)           # fourth argument in
                                    # fourth arg. register
        s.d     $f8,0(a5)           # save in 6th arg. reg

Extra Floating point Registers

As explained in Chapter 3, "N32 Compatibility, Porting, and Assembly Language Programming Issues," floating point registers are 64 bits wide under n32. They are no longer accessed as pairs of single precision registers for double precision calculations. As a result, the section of code that uses the pairs of lwc1 or swc1 instructions must be changed. The simplest way to accomplish this is to use the l.d assembly language instruction. This instruction expands to two lwc1 instructions under -mips1; under -mips2 and above, it expands to the ldc1 instruction.


Putting it together

The new version of regs.s is shown below. It is coded so that it will compile and execute for either o32 or n32 environments.

/* regs.s */
#include <sys/regdef.h>
#include <sys/asm.h>

.text

LOCALSZ=5            # save ra, a4, a5, gp, $f15

FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK

RAOFF=FRAMESZ-(1*SZREG)     # stack offset where ra is saved
A4OFF=FRAMESZ-(2*SZREG)     # stack offset where a4 is saved
A5OFF=FRAMESZ-(3*SZREG)     # stack offset where a5 is saved
GPOFF=FRAMESZ-(4*SZREG)     # stack offset where gp is saved
FPOFF=FRAMESZ-(5*SZREG)     # stack offset where $f15 is    
                            # saved
                            # a4, a5, and $f15 don't have to
                            # be saved, but no harm done in
                            # doing so
NESTED(regs, FRAMESZ, ra)
                            # define regs to be a nested
                            # function
    SETUP_GP                # used for caller saved gp
    PTR_SUBU sp,FRAMESZ     # setup stack frame
    SETUP_GP64(GPOFF, regs) # used for callee saved gp
    SAVE_GP(GPOFF)          # used for caller saved gp

    REG_S   ra, RAOFF(sp)   # save ra on stack

#if (_MIPS_SIM != _MIPS_SIM_ABI32)    
                            # not needed for o32
    REG_S   a4, A4OFF(sp)   # save a4 on stack (argument 4)
    REG_S   a5, A5OFF(sp)   # save a5 on stack (argument 5)
    s.d     $f15,FPOFF(sp)  # save $f15 on stack (argument 6)
#endif /* _MIPS_SIM != _MIPS_SIM_ABI32 */

    sw      gp, 0(a0)       # return gp in first arg
    sw      ra, 0(a1)       # return ra in second arg
    sw      sp, 0(a2)       # return sp in third arg

    li      a0, 1000        # call malloc 
    jal     malloc          # for illustration purposes only

    move    a0, v0          # call free
    jal     free            # go into libc.so twice
                                # this is why a4, a5, $f15
                                # had to be saved

#if (_MIPS_SIM != _MIPS_SIM_ABI32)
                                # not needed for o32
        l.d     $f15,FPOFF(sp)  # restore $f15 (argument #6)
        REG_L   a4, A4OFF(sp)   # restore a4   (argument #4)
        REG_L   a5, A5OFF(sp)   # restore a5   (argument #5)
#endif /* _MIPS_SIM != _MIPS_SIM_ABI32 */

#if (_MIPS_SIM == _MIPS_SIM_ABI32)   
                                # for o32 arguments will
                                # need to be pulled from the
                                # stack
        lw      t0,FRAMESZ+24(sp) # fifth argument is 24
                                  # relative to original sp
        l.d     $f4,0(t0)         # use l.d for correct code
                                  # on both mips1 & mips2
        l.d     $f6,FRAMESZ+16(sp)  # fourth argument is 16
                                    # relative to original sp
        add.d   $f8, $f4, $f6       # do the calculation
        lw      t0,FRAMESZ+28(sp)   # sixth argument is 28
                                    # relative to original sp
        s.d     $f8,0(t0)           # save the result there
#else
                                    # n32 args are in regs 
        mov.d   $f4,$f15            # 5th argument in 5th fp
                                    # arg. register
        l.d     $f6,0(a4)           # fourth argument in
                                    # fourth arg. register
        add.d   $f8, $f4, $f6       # do the calculation
        s.d     $f8,0(a5)           # save in 6th arg. reg
#endif /* _MIPS_SIM != _MIPS_SIM_ABI32 */

        REG_L   ra, RAOFF(sp)     # restore return address
        RESTORE_GP64              # restore gp for n32 
                                  # (callee saved)
        PTR_ADDU sp,FRAMESZ       # pop stack
        j        ra               # return to caller
.endregs

Next | Prev | Up | Top | Contents | Index