From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24405 invoked by alias); 10 Mar 2002 06:58:58 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 24116 invoked from network); 10 Mar 2002 06:57:48 -0000 Received: from unknown (HELO nevyn.them.org) (128.2.145.6) by sources.redhat.com with SMTP; 10 Mar 2002 06:57:46 -0000 Received: from drow by nevyn.them.org with local (Exim 3.35 #1 (Debian)) id 16jxG1-00036o-00; Sun, 10 Mar 2002 01:56:37 -0500 Date: Sat, 09 Mar 2002 22:58:00 -0000 From: Daniel Jacobowitz To: Andrew Cagney Cc: gdb-patches@sources.redhat.com Subject: Re: [RFA] mips: Fix "info registers" output Message-ID: <20020310015637.A13373@nevyn.them.org> Mail-Followup-To: Andrew Cagney , gdb-patches@sources.redhat.com References: <20010619225007.A10141@nevyn.them.org> <20020307165956.A22042@nevyn.them.org> <3C8ABF59.7080908@cygnus.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <3C8ABF59.7080908@cygnus.com> User-Agent: Mutt/1.3.23i X-SW-Source: 2002-03/txt/msg00134.txt.bz2 On Sat, Mar 09, 2002 at 09:05:13PM -0500, Andrew Cagney wrote: > >Meanwhile, Andrew, is this trivial fix OK? It just makes us decode the > >actual register values instead of two host pointers in our call to > >unpack_double. > > I suspect the code was more broken then you thought! I don't know about that. I pretty much rewrote it the first time I tried to fix this, last June. > If my memory is correct, the value is always stored LE in the register > pair. The code manages to do a double swap and consequently doesn't > correctly re-order the registers in the dbl_buffer. > > It is also really broken when a 32 bit ABI is on a 64 bit ISA. (It very > nearly corrupts the stack). I suppose... > > /* copy the two floats into one double, and unpack both */ > >- memcpy (dbl_buffer, raw_buffer, 2 * REGISTER_RAW_SIZE (FP0_REGNUM)); > > flt1 = unpack_double (builtin_type_float, raw_buffer[HI], &inv1); > > flt2 = unpack_double (builtin_type_float, raw_buffer[LO], &inv2); > >+ memcpy (dbl_buffer, raw_buffer[HI], REGISTER_RAW_SIZE (FP0_REGNUM)); > >+ memcpy (dbl_buffer + REGISTER_RAW_SIZE (FP0_REGNUM), > >+ raw_buffer[LO], REGISTER_RAW_SIZE (FP0_REGNUM)); > > doub = unpack_double (builtin_type_double, dbl_buffer, &inv3); > > > > I suspect a bigger improvement would look something like (I'm still not > 100% sure of what goes where with BE/LE though :-) > > if (!FP_REGISTER_DOUBLE) FP_REGISTER_DOUBLE describes a property of the ABI. I don't really think it's the appropriate check when printing floating-point registers; we always take care to print the single-precision value even if FP_REGISTER_DOUBLE, because they might be used in single-precision anyway. I sat down with an old patch from Don Howard (approved, but never committed) and my old patches and thought about this for a bit. I fixed all the mentioned nits with both patches, and rethought some of the design, and here's what I came up with. The comments in the code speak for themselves fairly well: +/* Floating point register management. + + Background: MIPS1 & 2 fp registers are 32 bits wide. To support + 64bit operations, these early MIPS cpus treat fp register pairs + (f0,f1) as a single register (d0). Later MIPS cpu's have 64 bit fp + registers and offer a compatibility mode that emulates the MIPS2 fp + model. When operating in MIPS2 fp compat mode, later cpu's split + double precision floats into two 32-bit chunks and store them in + consecutive fp regs. To display 64-bit floats stored in this + fashion, we have to combine 32 bits from f0 and 32 bits from f1. + Throw in user-configurable endianness and you have a real mess. + + The way this works is: + - If we are in 32-bit mode or on a 32-bit processor, then a 64-bit + double-precision value will be split across two logical registers. + The lower-numbered logical register will hold the low-order bits, + regardless of the processor's endianness. + - If we are on a 64-bit processor, and we are looking for a + single-precision value, it will be in the low ordered bits + of a 64-bit GPR (after mfc1, for example) or a 64-bit register + save slot in memory. + - If we are in 64-bit mode, everything is straightforward. I add two functions, mips_read_fp_register_single and mips_read_fp_register_double, that understand this. In the process, I eliminated the next-to-last use of REGISTER_CONVERT_TO_TYPE, and simplified a lot of code. Behaves correctly on the only target I have handy, which is a true 32-bit machine. Andrew, do you like the look of this? (Don's in the changelog because I cribbed one function and a lot of comments from him.) -- Daniel Jacobowitz Carnegie Mellon University MontaVista Software Debian GNU/Linux Developer 2002-03-10 Daniel Jacobowitz Don Howard * mips-tdep.c (ST0_FR): Define. (mips2_fp_compat): New function. (mips_read_fp_register_single): New function. (mips_read_fp_register_double): New function. (mips_print_register): Use them. (do_fp_register_row): Likewise. Index: mips-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/mips-tdep.c,v retrieving revision 1.66 diff -u -r1.66 mips-tdep.c --- mips-tdep.c 2002/02/20 22:51:41 1.66 +++ mips-tdep.c 2002/03/10 06:43:35 @@ -44,6 +44,10 @@ #include "elf-bfd.h" #include "symcat.h" +/* A useful bit in the CP0 status register (PS_REGNUM). */ +/* This bit is set if we are emulating 32-bit FPRs on a 64-bit chip. */ +#define ST0_FR (1 << 26) + /* The sizes of floating point registers. */ enum @@ -174,6 +178,27 @@ return 4; } +/* Determine if a MIPS3 or later cpu is operating in MIPS{1,2} FPU + compatiblity mode. A return value of 1 means that we have + physical 64-bit registers, but should treat them as 32-bit registers. */ + +static int +mips2_fp_compat (void) +{ + /* MIPS1 and MIPS2 have only 32 bit FPRs, and the FR bit is not + meaningful. */ + if (REGISTER_RAW_SIZE (FP0_REGNUM) == 4) + return 0; + + /* Otherwise check the FR bit in the status register - it controls + the FP compatiblity mode. If it is clear we are in compatibility + mode. */ + if ((read_register (PS_REGNUM) & ST0_FR) == 0) + return 1; + + return 0; +} + /* Indicate that the ABI makes use of double-precision registers provided by the FPU (rather than combining pairs of registers to form double-precision values). Do not use "TARGET_IS_MIPS64" to @@ -257,6 +282,9 @@ static CORE_ADDR after_prologue (CORE_ADDR pc, mips_extra_func_info_t proc_desc); +static void mips_read_fp_register_single (int regno, char *rare_buffer); +static void mips_read_fp_register_double (int regno, char *rare_buffer); + /* This value is the model of MIPS in use. It is derived from the value of the PrID register. */ @@ -2676,7 +2704,103 @@ } } +/* Floating point register management. + + Background: MIPS1 & 2 fp registers are 32 bits wide. To support + 64bit operations, these early MIPS cpus treat fp register pairs + (f0,f1) as a single register (d0). Later MIPS cpu's have 64 bit fp + registers and offer a compatibility mode that emulates the MIPS2 fp + model. When operating in MIPS2 fp compat mode, later cpu's split + double precision floats into two 32-bit chunks and store them in + consecutive fp regs. To display 64-bit floats stored in this + fashion, we have to combine 32 bits from f0 and 32 bits from f1. + Throw in user-configurable endianness and you have a real mess. + + The way this works is: + - If we are in 32-bit mode or on a 32-bit processor, then a 64-bit + double-precision value will be split across two logical registers. + The lower-numbered logical register will hold the low-order bits, + regardless of the processor's endianness. + - If we are on a 64-bit processor, and we are looking for a + single-precision value, it will be in the low ordered bits + of a 64-bit GPR (after mfc1, for example) or a 64-bit register + save slot in memory. + - If we are in 64-bit mode, everything is straightforward. + + Note that this code only deals with "live" registers at the top of the + stack. We will attempt to deal with saved registers later, when + the raw/cooked register interface is in place. (We need a general + interface that can deal with dynamic saved register sizes -- fp + regs could be 32 bits wide in one frame and 64 on the frame above + and below). */ + +/* Copy a 32-bit single-precision value from the current frame + into rare_buffer. */ + static void +mips_read_fp_register_single (int regno, char *rare_buffer) +{ + int raw_size = REGISTER_RAW_SIZE (regno); + char *raw_buffer = alloca (raw_size); + + if (read_relative_register_raw_bytes (regno, raw_buffer)) + error ("can't read register %d (%s)", regno, REGISTER_NAME (regno)); + if (raw_size == 8) + { + /* We have a 64-bit value for this register. Find the low-order + 32 bits. */ + int offset; + + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + offset = 4; + else + offset = 0; + + memcpy (rare_buffer, raw_buffer + offset, 4); + } + else + { + memcpy (rare_buffer, raw_buffer, 4); + } +} + +/* Copy a 64-bit double-precision value from the current frame into + rare_buffer. This may include getting half of it from the next + register. */ + +static void +mips_read_fp_register_double (int regno, char *rare_buffer) +{ + int raw_size = REGISTER_RAW_SIZE (regno); + + if (raw_size == 8 && !mips2_fp_compat ()) + { + /* We have a 64-bit value for this register, and we should use + all 64 bits. */ + if (read_relative_register_raw_bytes (regno, rare_buffer)) + error ("can't read register %d (%s)", regno, REGISTER_NAME (regno)); + } + else + { + if ((regno - FP0_REGNUM) & 1) + internal_error (__FILE__, __LINE__, + "mips_read_fp_register_double: bad access to " + "odd-numbered FP register"); + + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + { + mips_read_fp_register_single (regno, rare_buffer + 4); + mips_read_fp_register_single (regno + 1, rare_buffer); + } + else + { + mips_read_fp_register_single (regno, rare_buffer); + mips_read_fp_register_single (regno + 1, rare_buffer + 4); + } + } +} + +static void mips_print_register (int regnum, int all) { char raw_buffer[MAX_REGISTER_RAW_SIZE]; @@ -2688,22 +2812,23 @@ return; } - /* If an even floating point register, also print as double. */ + /* If we have a actual 32-bit floating point register (or we are in + 32-bit compatibility mode), and the register is even-numbered, + also print it as a double (spanning two registers). */ if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (regnum)) == TYPE_CODE_FLT + && (REGISTER_RAW_SIZE (regnum) == 4 + || mips2_fp_compat ()) && !((regnum - FP0_REGNUM) & 1)) - if (REGISTER_RAW_SIZE (regnum) == 4) /* this would be silly on MIPS64 or N32 (Irix 6) */ - { - char dbuffer[2 * MAX_REGISTER_RAW_SIZE]; + { + char dbuffer[2 * MAX_REGISTER_RAW_SIZE]; - read_relative_register_raw_bytes (regnum, dbuffer); - read_relative_register_raw_bytes (regnum + 1, dbuffer + MIPS_REGSIZE); - REGISTER_CONVERT_TO_TYPE (regnum, builtin_type_double, dbuffer); + mips_read_fp_register_double (regnum, dbuffer); - printf_filtered ("(d%d: ", regnum - FP0_REGNUM); - val_print (builtin_type_double, dbuffer, 0, 0, - gdb_stdout, 0, 1, 0, Val_pretty_default); - printf_filtered ("); "); - } + printf_filtered ("(d%d: ", regnum - FP0_REGNUM); + val_print (builtin_type_double, dbuffer, 0, 0, + gdb_stdout, 0, 1, 0, Val_pretty_default); + printf_filtered ("); "); + } fputs_filtered (REGISTER_NAME (regnum), gdb_stdout); /* The problem with printing numeric register names (r26, etc.) is that @@ -2717,8 +2842,10 @@ /* If virtual format is floating, print it that way. */ if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (regnum)) == TYPE_CODE_FLT) - if (FP_REGISTER_DOUBLE) - { /* show 8-byte floats as float AND double: */ + if (REGISTER_RAW_SIZE (regnum) == 8 && !mips2_fp_compat ()) + { + /* We have a meaningful 64-bit value in this register. Show + it as a 32-bit float and a 64-bit double. */ int offset = 4 * (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG); printf_filtered (" (float) "); @@ -2753,35 +2880,25 @@ static int do_fp_register_row (int regnum) { /* do values for FP (float) regs */ - char *raw_buffer[2]; - char *dbl_buffer; - /* use HI and LO to control the order of combining two flt regs */ - int HI = (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG); - int LO = (TARGET_BYTE_ORDER != BFD_ENDIAN_BIG); + char *raw_buffer; double doub, flt1, flt2; /* doubles extracted from raw hex data */ int inv1, inv2, inv3; - raw_buffer[0] = (char *) alloca (REGISTER_RAW_SIZE (FP0_REGNUM)); - raw_buffer[1] = (char *) alloca (REGISTER_RAW_SIZE (FP0_REGNUM)); - dbl_buffer = (char *) alloca (2 * REGISTER_RAW_SIZE (FP0_REGNUM)); + raw_buffer = (char *) alloca (2 * REGISTER_RAW_SIZE (FP0_REGNUM)); - /* Get the data in raw format. */ - if (read_relative_register_raw_bytes (regnum, raw_buffer[HI])) - error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum)); - if (REGISTER_RAW_SIZE (regnum) == 4) - { - /* 4-byte registers: we can fit two registers per row. */ - /* Also print every pair of 4-byte regs as an 8-byte double. */ - if (read_relative_register_raw_bytes (regnum + 1, raw_buffer[LO])) - error ("can't read register %d (%s)", - regnum + 1, REGISTER_NAME (regnum + 1)); - - /* copy the two floats into one double, and unpack both */ - memcpy (dbl_buffer, raw_buffer, 2 * REGISTER_RAW_SIZE (FP0_REGNUM)); - flt1 = unpack_double (builtin_type_float, raw_buffer[HI], &inv1); - flt2 = unpack_double (builtin_type_float, raw_buffer[LO], &inv2); - doub = unpack_double (builtin_type_double, dbl_buffer, &inv3); + if (REGISTER_RAW_SIZE (regnum) == 4 || mips2_fp_compat ()) + { + /* 4-byte registers: we can fit two registers per row. */ + /* Also print every pair of 4-byte regs as an 8-byte double. */ + mips_read_fp_register_single (regnum, raw_buffer); + flt1 = unpack_double (builtin_type_float, raw_buffer, &inv1); + + mips_read_fp_register_single (regnum + 1, raw_buffer); + flt2 = unpack_double (builtin_type_float, raw_buffer, &inv2); + mips_read_fp_register_double (regnum, raw_buffer); + doub = unpack_double (builtin_type_double, raw_buffer, &inv3); + printf_filtered (" %-5s", REGISTER_NAME (regnum)); if (inv1) printf_filtered (": "); @@ -2805,14 +2922,14 @@ regnum += 2; } else - { /* eight byte registers: print each one as float AND as double. */ - int offset = 4 * (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG); - - memcpy (dbl_buffer, raw_buffer[HI], 2 * REGISTER_RAW_SIZE (FP0_REGNUM)); - flt1 = unpack_double (builtin_type_float, - &raw_buffer[HI][offset], &inv1); - doub = unpack_double (builtin_type_double, dbl_buffer, &inv3); - + { + /* Eight byte registers: print each one as float AND as double. */ + mips_read_fp_register_single (regnum, raw_buffer); + flt1 = unpack_double (builtin_type_double, raw_buffer, &inv1); + + mips_read_fp_register_double (regnum, raw_buffer); + doub = unpack_double (builtin_type_double, raw_buffer, &inv3); + printf_filtered (" %-5s: ", REGISTER_NAME (regnum)); if (inv1) printf_filtered ("");