This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
MIPS asm prologue macros
- From: Masao Uebayashi <uebayasi at gmail dot com>
- To: binutils at sources dot redhat dot com
- Date: Mon, 02 Jun 2008 17:02:18 +0900 (JST)
- Subject: MIPS asm prologue macros
Hi!
The "MIPSpro(TM) N32 ABI Handbook" shows an attached code (slightly
cleaned up by me) to explain how to write assembly for ABIs
({O,N}{32,64}). The start of a sample assembly function looks like:
NESTED(asmfunc, FRAMESZ, ra)
move t0, gp # save entering gp
# SIM_ABI64 has gp callee save
# no harm for SIM_ABI32
SETUP_GPX(t8)
PTR_SUBU sp, FRAMESZ
SETUP_GP64(GPOFF, asmfunc)
SAVE_GP(GPOFF)
:
Questions:
- I'm not sure where / when the t0 value is restored into GP.
- Has anyone invented any macros which can handle all the four ABI
cases cleanly?
Masao
Using a Different Subroutine Linkage
------------------------------------
Under n32, more registers are used to pass arguments to called
subroutines. The registers that are saved by the calling and called
subroutines are also different under this convention, which is
described in detail in Chapter 2, "Calling Convention
Implementations", page 5. As a result, a different register naming
convention exists. The compiler predefines _MIPS_SIM and this enables
macros in <sys/asm.h> and <sys/regdef.h>. Some important
ramifications of the subroutine linkage convention are outlined in the
following.
The _MIPS_SIM_NABI32 model (n32), defines four additional argument
registers for a total of eight argument registers: $4..$11. The
additional four argument registers come at the expense of the temp
registers in <sys/regdef.h>. In this model, there are no registers
t4..t7, so any code using these registers does not compile under this
model. Similarly, the register names a4..a7 are not available under
the _MIPS_SIM_ABI32 model. (Note that those temporary registers are
not lost; the argument registers can serve as scratch registers also,
with certain constraints.)
To make it easier to convert assembler code, the new names ta0, ta1,
ta2, and ta3 are available under both _MIPS_SIM models. These alias
with t4..t7 in the o32 ABI, and with a4..a7 in the n32 ABI.
Another facet of the linkage convention is that the caller no longer
has to reserve space for a called function in which to store its
arguments. The called routine allocates space for storing its
arguments on its own stack, if desired. The NARGSAVE define in
<sys/asm.h> helps with this. The following example handles assembly
language coding issues for n32 and KPIC (KPIC requires that the asm
coder deals with PIC issues). It creates a template for the start and
end of a generic assembly language routine. The template is followed
by relevant defines and macros from <sys/asm.h>.
#include <sys/regdef.h>
#include <sys/asm.h>
#include <sys/fpregdef.h>
LOCALSZ= 7 # save gp ra and any other needed registers
/* For this example 7 items are saved on the stack */
/* To access the appropriate item use the offsets below */
FRAMESZ= (((NARGSAVE + LOCALSZ) * SZREG) + ALSZ) & ALMASK
RAOFF= FRAMESZ - (1 * SZREG)
GPOFF= FRAMESZ - (4 * SZREG)
A0OFF= FRAMESZ - (5 * SZREG)
A1OFF= FRAMESZ - (6 * SZREG)
T0OFF= FRAMESZ - (7 * SZREG)
NESTED(asmfunc, FRAMESZ, ra)
move t0, gp # save entering gp
# SIM_ABI64 has gp callee save
# no harm for SIM_ABI32
SETUP_GPX(t8)
PTR_SUBU sp, FRAMESZ
SETUP_GP64(GPOFF, asmfunc)
SAVE_GP(GPOFF)
/* Save registers as needed here */
REG_S ra, RAOFF(sp)
REG_S a0, A0OFF(sp)
REG_S a1, A1OFF(sp)
REG_S t0, T0OFF(sp)
/* do real work here */
/* safe to call other functions */
/* restore saved regsisters as needed here */
REG_L ra, RAOFF(sp)
REG_L a0, A0OFF(sp)
REG_L a1, A1OFF(sp)
REG_L t0, T0OFF(sp)
/* setup return address, $gp and stack pointer */
REG_L ra, RAOFF(sp)
RESTORE_GP64
PTR_ADDU sp, FRAMESZ
bne v0, zero, err
j ra
END(asmfunc)
/*
* The following macro definitions are
* from /usr/include/sys/asm.h */
*/
#if (_MIPS_SIM == _MIPS_SIM_ABI32)
/*
* Set gp when at 1st instruction
*/
#define SETUP_GP \
.set noreorder; \
.cpload t9; \
.set reorder
/* Set gp when not at 1st instruction */
#define SETUP_GPX(tmp) \
.set noreorder; \
move tmp, ra; /* save old ra */ \
bal 10f; /* find addr of cpload */ \
nop; \
10: .cpload ra; \
move ra, tmp; \
.set reorder;
#define SETUP_GPX_L(tmp, l) \
.set noreorder; \
move tmp, ra; /* save old ra */ \
bal l; /* find addr of cpload */ \
nop; \
l: .cpload ra; \
move ra, tmp; \
.set reorder;
#define SAVE_GP(x) \
.cprestore x; /* save gp trigger t9/jalr conversion */
#define SETUP_GP64(offset, func)
#define SETUP_GPX64(offset, tmp)
#define SETUP_GPX64_L(offset, tmp, l)
#define RESTORE_GP64
#define USE_ALT_CP(reg)
#else /* (_MIPS_SIM == _MIPS_SIM_ABI64) || (_MIPS_SIM == _MIPS_SIM_NABI32) */
/*
* For callee-saved gp calling convention:
*/
#define SETUP_GP
#define SETUP_GPX(tmp)
#define SETUP_GPX_L(tmp, l)
#define SAVE_GP(x)
#define SETUP_GP64(offset, func) \
.cpsetup t9, offset, func
#define SETUP_GPX64(offset, tmp) \
move tmp, ra; /* save old ra */ \
.set noreorder; \
bal 10f; /* find addr of .cpsetup */ \
nop; \
10: .set reorder; \
.cpsetup ra, offset, 10b; \
move ra, tmp
#define SETUP_GPX64_L(offset, tmp, l) \
move tmp, ra; /* save old ra */ \
.set noreorder; \
bal l; /* find addr of .cpsetup */ \
nop; \
l: .set reorder; \
.cpsetup ra, offset, l; \
move ra, tmp
#define RESTORE_GP64 \
.cpreturn
#define USE_ALT_CP(reg) \
.cplocal reg /* use alternate register for context pointer */
#endif /* _MIPS_SIM != _MIPS_SIM_ABI32 */
/*
* Stack Frame Definitions
*/
#if (_MIPS_SIM == _MIPS_SIM_ABI32)
#define NARGSAVE 4 /* space for 4 arg regs must be alloc*/
#endif
#if (_MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32)
#define NARGSAVE 0 /* no caller responsibilities */
#endif
#define ALSZ 15 /* align on 16 byte boundary */
#define ALMASK (~0xf)
#if (_MIPS_ISA == _MIPS_ISA_MIPS1 || _MIPS_ISA == _MIPS_ISA_MIPS2)
#define SZREG 4
#endif
#if (_MIPS_ISA == _MIPS_ISA_MIPS3 || _MIPS_ISA == _MIPS_ISA_MIPS4)
#define SZREG 8
#endif