This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Fix MIPS ABI issues
- From: "Joseph S. Myers" <joseph at codesourcery dot com>
- To: gdb-patches at sourceware dot org
- Date: Fri, 18 Jul 2008 15:39:25 +0000 (UTC)
- Subject: Fix MIPS ABI issues
This patch fixes various issues in GDB's handling of MIPS function call
ABIs shown up by the testsuite:
* For O32, when a float value is passed in an FPR, only one GPR is
skipped, not two, contradicting the guess given in a comment. (The psABI
document, such as it is, has examples confirming this.)
* For (hard-float) N32 and N64, 128-bit long double values are passed in
even-odd pairs of floating-point registers; GDB was failing to handle
passing them at all. Confirmed in the N32 ABI document (I don't have a
psABI document for N64 that discusses the function call and return
interface).
* For N32 and N64, float parameters not passed in FPRs are left-justified,
not right-justified. The N32 ABI document explicitly says this.
* For N32 and N64, the special return convention for structs containing
one or two floating-point values also applies to soft-float (using GPRs
instead of FPRs). The N32 ABI document does not discuss soft-float; this
is what GCC does.
* For N32 and N64, that special return convention applies to a struct
containing a 128-bit long double (which needs two registers). The N32 ABI
document is silent about this case; this is what GCC does. Both GCC and
GDB already agree that a struct larger than 128 bits is returned in
memory, so this case only applies for a struct containing a single long
double.
Tested for mips-linux-gnu (using GCC 4.3), big and little endian, hard and
soft float, O32, N32 and N64, all combinations except for little-endian
soft-float N32 and N64 for which I don't have libraries to hand. No
regressions, and this fixes
FAIL: gdb.base/callfuncs.exp: Call function with many float arguments.
for O32 hard float both endian and N32/N64 big-endian both hard and soft
float;
FAIL: gdb.base/call-sc.exp: p/c L; call call-sc-tld
FAIL: gdb.base/structs.exp: finish foo<n>; return 1 structs-tld (GDB internal error)
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 1 structs-tld (GDB internal error)
FAIL: gdb.base/structs.exp: return foo<n>; return 1 structs-tld (GDB internal error)
FAIL: gdb.base/structs.exp: value foo<n> finished; return 1 structs-tld
FAIL: gdb.base/structs.exp: value foo<n> returned; return 1 structs-tld
for N32 and N64 hard float both endian; and
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 1 structs-tf
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 2 structs-td-tf
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 2 structs-tf
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 2 structs-tf-td
FAIL: gdb.base/structs.exp: value foo<n> finished; return 1 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> finished; return 2 structs-td-tf
FAIL: gdb.base/structs.exp: value foo<n> finished; return 2 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> finished; return 2 structs-tf-td
FAIL: gdb.base/structs.exp: value foo<n> returned; return 1 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> returned; return 2 structs-td-tf
FAIL: gdb.base/structs.exp: value foo<n> returned; return 2 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> returned; return 2 structs-tf-td
for N32 and N64 soft float. OK to commit?
2008-07-18 Joseph Myers <joseph@codesourcery.com>
* mips-tdep.c (mips_n32n64_push_dummy_call): Handle passing
128-bit long doubles in even-odd pairs of FPRs. Do not
right-align float arguments for big-endian.
(mips_n32n64_return_value): Apply return value convention for
structs containing one or two floating-point values to soft-float
as well as hard-float. Handle 128-bit long doubles in such
structs.
(mips_o32_push_dummy_call): Only skip one integer register for a
float argument passed in an FPR.
Index: gdb/mips-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mips-tdep.c,v
retrieving revision 1.476
diff -u -p -r1.476 mips-tdep.c
--- gdb/mips-tdep.c 14 Jul 2008 11:25:11 -0000 1.476
+++ gdb/mips-tdep.c 18 Jul 2008 13:35:45 -0000
@@ -3142,23 +3142,49 @@ mips_n32n64_push_dummy_call (struct gdba
val = value_contents (arg);
+ /* A 128-bit long double value requires an even-odd pair of
+ floating-point registers. */
+ if (len == 16
+ && fp_register_arg_p (gdbarch, typecode, arg_type)
+ && (float_argreg & 1))
+ {
+ float_argreg++;
+ argreg++;
+ }
+
if (fp_register_arg_p (gdbarch, typecode, arg_type)
&& argreg <= MIPS_LAST_ARG_REGNUM (gdbarch))
{
/* This is a floating point value that fits entirely
- in a single register. */
- LONGEST regval = extract_unsigned_integer (val, len);
+ in a single register or a pair of registers. */
+ int reglen = (len <= MIPS64_REGSIZE ? len : MIPS64_REGSIZE);
+ LONGEST regval = extract_unsigned_integer (val, reglen);
if (mips_debug)
fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
- float_argreg, phex (regval, len));
+ float_argreg, phex (regval, reglen));
regcache_cooked_write_unsigned (regcache, float_argreg, regval);
if (mips_debug)
fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
- argreg, phex (regval, len));
+ argreg, phex (regval, reglen));
regcache_cooked_write_unsigned (regcache, argreg, regval);
float_argreg++;
argreg++;
+ if (len == 16)
+ {
+ regval = extract_unsigned_integer (val + reglen, reglen);
+ if (mips_debug)
+ fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
+ float_argreg, phex (regval, reglen));
+ regcache_cooked_write_unsigned (regcache, float_argreg, regval);
+
+ if (mips_debug)
+ fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
+ argreg, phex (regval, reglen));
+ regcache_cooked_write_unsigned (regcache, argreg, regval);
+ float_argreg++;
+ argreg++;
+ }
}
else
{
@@ -3199,8 +3225,7 @@ mips_n32n64_push_dummy_call (struct gdba
if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
{
if ((typecode == TYPE_CODE_INT
- || typecode == TYPE_CODE_PTR
- || typecode == TYPE_CODE_FLT)
+ || typecode == TYPE_CODE_PTR)
&& len <= 4)
longword_offset = MIPS64_REGSIZE - len;
}
@@ -3389,15 +3414,16 @@ mips_n32n64_return_value (struct gdbarch
&& (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 0)))
== TYPE_CODE_FLT)
&& (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 1)))
- == TYPE_CODE_FLT)))
- && tdep->mips_fpu_type != MIPS_FPU_NONE)
+ == TYPE_CODE_FLT))))
{
/* A struct that contains one or two floats. Each value is part
in the least significant part of their floating point
- register.. */
+ register (or GPR, for soft float). */
int regnum;
int field;
- for (field = 0, regnum = mips_regnum (gdbarch)->fp0;
+ for (field = 0, regnum = (tdep->mips_fpu_type != MIPS_FPU_NONE
+ ? mips_regnum (gdbarch)->fp0
+ : MIPS_V0_REGNUM);
field < TYPE_NFIELDS (type); field++, regnum += 2)
{
int offset = (FIELD_BITPOS (TYPE_FIELDS (type)[field])
@@ -3405,11 +3431,27 @@ mips_n32n64_return_value (struct gdbarch
if (mips_debug)
fprintf_unfiltered (gdb_stderr, "Return float struct+%d\n",
offset);
- mips_xfer_register (gdbarch, regcache,
- gdbarch_num_regs (gdbarch) + regnum,
- TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)),
- gdbarch_byte_order (gdbarch),
- readbuf, writebuf, offset);
+ if (TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)) == 16)
+ {
+ /* A 16-byte long double field goes in two consecutive
+ registers. */
+ mips_xfer_register (gdbarch, regcache,
+ gdbarch_num_regs (gdbarch) + regnum,
+ 8,
+ gdbarch_byte_order (gdbarch),
+ readbuf, writebuf, offset);
+ mips_xfer_register (gdbarch, regcache,
+ gdbarch_num_regs (gdbarch) + regnum + 1,
+ 8,
+ gdbarch_byte_order (gdbarch),
+ readbuf, writebuf, offset + 8);
+ }
+ else
+ mips_xfer_register (gdbarch, regcache,
+ gdbarch_num_regs (gdbarch) + regnum,
+ TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)),
+ gdbarch_byte_order (gdbarch),
+ readbuf, writebuf, offset);
}
return RETURN_VALUE_REGISTER_CONVENTION;
}
@@ -3612,15 +3654,13 @@ mips_o32_push_dummy_call (struct gdbarch
fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
float_argreg, phex (regval, len));
regcache_cooked_write_unsigned (regcache, float_argreg++, regval);
- /* CAGNEY: 32 bit MIPS ABI's always reserve two FP
- registers for each argument. The below is (my
- guess) to ensure that the corresponding integer
- register has reserved the same space. */
+ /* Although two FP registers are reserved for each
+ argument, only one corresponding integer register is
+ reserved. */
if (mips_debug)
fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
argreg, phex (regval, len));
- regcache_cooked_write_unsigned (regcache, argreg, regval);
- argreg += 2;
+ regcache_cooked_write_unsigned (regcache, argreg++, regval);
}
/* Reserve space for the FP register. */
stack_offset += align_up (len, MIPS32_REGSIZE);
--
Joseph S. Myers
joseph@codesourcery.com