aboutsummaryrefslogtreecommitdiff
path: root/gdb/mips-tdep.c
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@linux-mips.org>2007-11-20 13:47:42 +0000
committerMaciej W. Rozycki <macro@linux-mips.org>2007-11-20 13:47:42 +0000
commit2207132da48e8347055233edd8cc6b8abffdd0a0 (patch)
treea3a528e1dd4d3034fde077b06b6b8a58d1da6182 /gdb/mips-tdep.c
parentcd76b0b7c4dcfe0167f65110ba652272d5e3ef87 (diff)
downloadgdb-2207132da48e8347055233edd8cc6b8abffdd0a0.zip
gdb-2207132da48e8347055233edd8cc6b8abffdd0a0.tar.gz
gdb-2207132da48e8347055233edd8cc6b8abffdd0a0.tar.bz2
* mips-tdep.c (mips16_scan_prologue): Handle the MIPS16e SAVE
instruction.
Diffstat (limited to 'gdb/mips-tdep.c')
-rw-r--r--gdb/mips-tdep.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 6df6c61..8be42fe 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -1500,6 +1500,7 @@ mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc,
unsigned short prev_inst = 0; /* saved copy of previous instruction */
unsigned inst = 0; /* current instruction */
unsigned entry_inst = 0; /* the entry instruction */
+ unsigned save_inst = 0; /* the save instruction */
int reg, offset;
int extend_bytes = 0;
@@ -1603,6 +1604,12 @@ mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc,
else if ((inst & 0xf81f) == 0xe809
&& (inst & 0x700) != 0x700) /* entry */
entry_inst = inst; /* save for later processing */
+ else if ((inst & 0xff80) == 0x6480) /* save */
+ {
+ save_inst = inst; /* save for later processing */
+ if (prev_extend_bytes) /* extend */
+ save_inst |= prev_inst << 16;
+ }
else if ((inst & 0xf800) == 0x1800) /* jal(x) */
cur_pc += MIPS_INSN16_SIZE; /* 32-bit instruction */
else if ((inst & 0xff1c) == 0x6704) /* move reg,$a0-$a3 */
@@ -1661,6 +1668,101 @@ mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc,
}
}
+ /* The SAVE instruction is similar to ENTRY, except that defined by the
+ MIPS16e ASE of the MIPS Architecture. Unlike with ENTRY though, the
+ size of the frame is specified as an immediate field of instruction
+ and an extended variation exists which lets additional registers and
+ frame space to be specified. The instruction always treats registers
+ as 32-bit so its usefulness for 64-bit ABIs is questionable. */
+ if (save_inst != 0 && mips_abi_regsize (gdbarch) == 4)
+ {
+ static int args_table[16] = {
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 2, 2, 2, 0, 3, 3, 4, -1,
+ };
+ static int astatic_table[16] = {
+ 0, 1, 2, 3, 0, 1, 2, 3,
+ 0, 1, 2, 4, 0, 1, 0, -1,
+ };
+ int aregs = (save_inst >> 16) & 0xf;
+ int xsregs = (save_inst >> 24) & 0x7;
+ int args = args_table[aregs];
+ int astatic = astatic_table[aregs];
+ long frame_size;
+
+ if (args < 0)
+ {
+ warning (_("Invalid number of argument registers encoded in SAVE."));
+ args = 0;
+ }
+ if (astatic < 0)
+ {
+ warning (_("Invalid number of static registers encoded in SAVE."));
+ astatic = 0;
+ }
+
+ /* For standard SAVE the frame size of 0 means 128. */
+ frame_size = ((save_inst >> 16) & 0xf0) | (save_inst & 0xf);
+ if (frame_size == 0 && (save_inst >> 16) == 0)
+ frame_size = 16;
+ frame_size *= 8;
+ frame_offset += frame_size;
+
+ /* Now we can calculate what the SP must have been at the
+ start of the function prologue. */
+ sp += frame_offset;
+
+ /* Check if A0-A3 were saved in the caller's argument save area. */
+ for (reg = MIPS_A0_REGNUM, offset = 0; reg < args + 4; reg++)
+ {
+ set_reg_offset (this_cache, reg, sp + offset);
+ offset += mips_abi_regsize (gdbarch);
+ }
+
+ offset = -4;
+
+ /* Check if the RA register was pushed on the stack. */
+ if (save_inst & 0x40)
+ {
+ set_reg_offset (this_cache, MIPS_RA_REGNUM, sp + offset);
+ offset -= mips_abi_regsize (gdbarch);
+ }
+
+ /* Check if the S8 register was pushed on the stack. */
+ if (xsregs > 6)
+ {
+ set_reg_offset (this_cache, 30, sp + offset);
+ offset -= mips_abi_regsize (gdbarch);
+ xsregs--;
+ }
+ /* Check if S2-S7 were pushed on the stack. */
+ for (reg = 18 + xsregs - 1; reg > 18 - 1; reg--)
+ {
+ set_reg_offset (this_cache, reg, sp + offset);
+ offset -= mips_abi_regsize (gdbarch);
+ }
+
+ /* Check if the S1 register was pushed on the stack. */
+ if (save_inst & 0x10)
+ {
+ set_reg_offset (this_cache, 17, sp + offset);
+ offset -= mips_abi_regsize (gdbarch);
+ }
+ /* Check if the S0 register was pushed on the stack. */
+ if (save_inst & 0x20)
+ {
+ set_reg_offset (this_cache, 16, sp + offset);
+ offset -= mips_abi_regsize (gdbarch);
+ }
+
+ /* Check if A0-A3 were pushed on the stack. */
+ for (reg = MIPS_A0_REGNUM + 3; reg > MIPS_A0_REGNUM + 3 - astatic; reg--)
+ {
+ set_reg_offset (this_cache, reg, sp + offset);
+ offset -= mips_abi_regsize (gdbarch);
+ }
+ }
+
if (this_cache != NULL)
{
this_cache->base =