[RFC] mips-tdep.c: Update mips_register_to_value(), et al...

Kevin Buettner
2010-12-08T23:47:08Z

Below is yet another patch that fixes a problem arising from the
simulator catching UNPREDICTABLE behavior. This time though, it's
for mips64 targets that are being run in 64-bit mode. (My earlier
two patches dealt with 32-bit mode.)

This patch fixes a failure in gdb.base/store.exp for mipsisa64-elf
with target_board set to either mips-sim-idt64/-EB/-mips64 or
mips-sim-idt64/-EL/-mips64.

The log file sequence leading to the failure is as follows:

tbreak wack_int
Temporary breakpoint 4 at 0xffffffff800205e0: file /ironwood1/sourceware-mips/mipsisa64-elf/../src/gdb/testsuite/gdb.base/store.c, line 85.
(gdb) PASS: gdb.base/store.exp: tbreak wack_int
continue
Continuing.

Temporary breakpoint 4, wack_int (u=-1, v=-2) at /ironwood1/sourceware-mips/mipsisa64-elf/../src/gdb/testsuite/gdb.base/store.c:85
85 register int l = u, r = v;
(gdb) PASS: gdb.base/store.exp: continue to wack_int
next
86 l = add_int (l, r);
(gdb) PASS: gdb.base/store.exp: var int l; next int
print l
$9 = -1
(gdb) PASS: gdb.base/store.exp: var int l; print old l, expecting -1
print r
$10 = -2
(gdb) PASS: gdb.base/store.exp: var int l; print old r, expecting -2
set variable l = 4
(gdb) PASS: gdb.base/store.exp: var int l; setting l to 4
print l
$11 = 4
(gdb) PASS: gdb.base/store.exp: var int l; print new l, expecting 4
next
UNPREDICTABLE: PC = 0x800203a4
Quit
(gdb) FAIL: gdb.base/store.exp: var int l; next over add call

When executing the command "set variable l = 4", GDB is writing to the
low 32 bits of the register containing `l'. The value was previously
-1. Writing only the low 32 bits with a positive value causes a
sign mismatch between the lower 32 bits and the upper 32 bits which
eventually causes the sim to abort back to GDB. The instruction
that causes the abort is the 32-bit addu instruction:

=> 0xffffffff800203a4 <add_int+20>: addu v0,v1,v0
(gdb) p/x $v0
$8 = 0xfffffffffffffffe
(gdb) p/x $v1
$9 = 0xffffffff00000004
(gdb) si
UNPREDICTABLE: PC = 0x800203a4

(If the 64-bit daddu had been used instead, this abort would not have
been triggered.)

The patch below adds a case to mips_value_to_register() to ensure that
sign extension is performed when writing a value shorter than 64-bits
to a 64-bit register. It updates mips_convert_register_p() and
mips_register_to_value() as well.

Comments?

(Can anyone think of better names for the two new functions that I
introduced?)

Kevin

* mips-tdep.c (big_endian_4_byte_fp_reg_with_double_type_p)
(eight_byte_gp_reg_with_shorter_type_p): New functions.
(mips_convert_register_p): Invoke new functions above.
(mips_register_to_value): Add case for fetching value shorter
than 64 bits from a 64-bit register.
(mips_value_to_register): Add case for storing value shorter
than 64 bits into a 64-bit register.

Index: mips-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mips-tdep.c,v
retrieving revision 1.508
diff -u -p -r1.508 mips-tdep.c
--- mips-tdep.c 28 Nov 2010 04:31:24 -0000 1.508
+++ mips-tdep.c 8 Dec 2010 22:49:57 -0000
@@ -626,7 +634,8 @@ set_mips64_transfers_32bit_regs (char *a
/* Convert to/from a register and the corresponding memory value. */

static int
-mips_convert_register_p (struct gdbarch *gdbarch, int regnum, struct type *type)
+big_endian_4_byte_fp_reg_with_double_type_p (struct gdbarch *gdbarch,
+ int regnum, struct type *type)
{
return (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG
&& register_size (gdbarch, regnum) == 4
@@ -637,20 +646,89 @@ mips_convert_register_p (struct gdbarch
&& TYPE_CODE (type) == TYPE_CODE_FLT && TYPE_LENGTH (type) == 8);
}

+static int
+eight_byte_gp_reg_with_shorter_type_p (struct gdbarch *gdbarch, int regnum,
+ struct type *type)
+{
+ int num_regs = gdbarch_num_regs (gdbarch);
+
+ return (register_size (gdbarch, regnum) == 8
+ && regnum % num_regs > 0 && regnum % num_regs < 32
+ && TYPE_LENGTH (type) < 8);
+}
+
+static int
+mips_convert_register_p (struct gdbarch *gdbarch, int regnum, struct type *type)
+{
+ return big_endian_4_byte_fp_reg_with_double_type_p (gdbarch, regnum, type)
+ || eight_byte_gp_reg_with_shorter_type_p (gdbarch, regnum, type);
+}
+
static void
mips_register_to_value (struct frame_info *frame, int regnum,
struct type *type, gdb_byte *to)
{
- get_frame_register (frame, regnum + 0, to + 4);
- get_frame_register (frame, regnum + 1, to + 0);
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+
+ if (big_endian_4_byte_fp_reg_with_double_type_p (gdbarch, regnum, type))
+ {
+ get_frame_register (frame, regnum + 0, to + 4);
+ get_frame_register (frame, regnum + 1, to + 0);
+ }
+ else if (eight_byte_gp_reg_with_shorter_type_p (gdbarch, regnum, type))
+ {
+ int len = TYPE_LENGTH (type);
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ get_frame_register_bytes (frame, regnum, 8 - len, len, to);
+ else
+ get_frame_register_bytes (frame, regnum, 0, len, to);
+ }
+ else
+ {
+ internal_error (__FILE__, __LINE__,
+ _("mips_register_to_value: unrecognized case"));
+ }
}

static void
mips_value_to_register (struct frame_info *frame, int regnum,
struct type *type, const gdb_byte *from)
{
- put_frame_register (frame, regnum + 0, from + 4);
- put_frame_register (frame, regnum + 1, from + 0);
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+
+ if (big_endian_4_byte_fp_reg_with_double_type_p (gdbarch, regnum, type))
+ {
+ put_frame_register (frame, regnum + 0, from + 4);
+ put_frame_register (frame, regnum + 1, from + 0);
+ }
+ else if (eight_byte_gp_reg_with_shorter_type_p (gdbarch, regnum, type))
+ {
+ gdb_byte fill[8];
+ int len = TYPE_LENGTH (type);
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ {
+ if (from[0] & 0x80)
+ store_signed_integer (fill, 8, BFD_ENDIAN_BIG, -1);
+ else
+ store_signed_integer (fill, 8, BFD_ENDIAN_BIG, 0);
+ put_frame_register_bytes (frame, regnum, 0, 8 - len, fill);
+ put_frame_register_bytes (frame, regnum, 8 - len, len, from);
+ }
+ else
+ {
+ if (from[len-1] & 0x80)
+ store_signed_integer (fill, 8, BFD_ENDIAN_LITTLE, -1);
+ else
+ store_signed_integer (fill, 8, BFD_ENDIAN_LITTLE, 0);
+ put_frame_register_bytes (frame, regnum, 0, len, from);
+ put_frame_register_bytes (frame, regnum, len, 8 - len, fill);
+ }
+ }
+ else
+ {
+ internal_error (__FILE__, __LINE__,
+ _("mips_value_to_register: unrecognized case"));
+ }
}

/* Return the GDB type object for the "standard" data type of data in