From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 517 invoked by alias); 18 Jul 2008 15:39:55 -0000 Received: (qmail 503 invoked by uid 22791); 18 Jul 2008 15:39:53 -0000 X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (65.74.133.4) by sourceware.org (qpsmtpd/0.31) with ESMTP; Fri, 18 Jul 2008 15:39:30 +0000 Received: (qmail 30516 invoked from network); 18 Jul 2008 15:39:27 -0000 Received: from unknown (HELO digraph.polyomino.org.uk) (joseph@127.0.0.2) by mail.codesourcery.com with ESMTPA; 18 Jul 2008 15:39:27 -0000 Received: from jsm28 (helo=localhost) by digraph.polyomino.org.uk with local-esmtp (Exim 4.68) (envelope-from ) id 1KJs3J-0007MG-Rp for gdb-patches@sourceware.org; Fri, 18 Jul 2008 15:39:25 +0000 Date: Fri, 18 Jul 2008 15:39:00 -0000 From: "Joseph S. Myers" To: gdb-patches@sourceware.org Subject: Fix MIPS ABI issues Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-07/txt/msg00364.txt.bz2 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; return 1 structs-tld (GDB internal error) FAIL: gdb.base/structs.exp: p/c fun(); call 1 structs-tld (GDB internal error) FAIL: gdb.base/structs.exp: return foo; return 1 structs-tld (GDB internal error) FAIL: gdb.base/structs.exp: value foo finished; return 1 structs-tld FAIL: gdb.base/structs.exp: value foo returned; return 1 structs-tld for N32 and N64 hard float both endian; and FAIL: gdb.base/structs.exp: p/c fun(); call 1 structs-tf FAIL: gdb.base/structs.exp: p/c fun(); call 2 structs-td-tf FAIL: gdb.base/structs.exp: p/c fun(); call 2 structs-tf FAIL: gdb.base/structs.exp: p/c fun(); call 2 structs-tf-td FAIL: gdb.base/structs.exp: value foo finished; return 1 structs-tf FAIL: gdb.base/structs.exp: value foo finished; return 2 structs-td-tf FAIL: gdb.base/structs.exp: value foo finished; return 2 structs-tf FAIL: gdb.base/structs.exp: value foo finished; return 2 structs-tf-td FAIL: gdb.base/structs.exp: value foo returned; return 1 structs-tf FAIL: gdb.base/structs.exp: value foo returned; return 2 structs-td-tf FAIL: gdb.base/structs.exp: value foo returned; return 2 structs-tf FAIL: gdb.base/structs.exp: value foo returned; return 2 structs-tf-td for N32 and N64 soft float. OK to commit? 2008-07-18 Joseph Myers * 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