[RFA] W.I.P. AltiVec ppc registers support.

Elena Zannoni ezannoni@cygnus.com
Sun Nov 18 13:27:00 GMT 2001


AltiVec registers are 32 128-bit wide registers found on the G4
powerpc processor family. This patch adds some initial support for
such registers to gdb on a linux ppc platform.

The Altivec registers are not displayed in a normal 'info reg' command
output. They are shown (like fp regs) if one says 'info all' instead.
Furthermore I added a specific 'info power altivec' command to display
just the Altivec registers. (I am not sure that the word 'power' is
the best choice, maybe simply 'ppc' or 'powerpc' is better).  This way
the command as just a specific powerpc info command, w/o it being a
generic info command avaliable on every platform.

If there is no kernel support for ptrace to handle the AltiVec
registers, they will display as 0's.

The little drawback is that such registers will be displayed any time
gdb's architecture is 'powerpc:common' or 'powerpc:7400'. The first
case is the architecture gdb defaults to when run on a ppc machine,
because the multiarch mechanism cannot determine the nature of the
executable file. To be clearer, there is no way for gdb to know that
the architecture is meant to be a ppc7400 based on executable
information provided by BFD (unlike for Mips and Sh). So, gdb will
always start and *stay* with a powerpc:common architecture selected,
unless the user explicitly sets the architecture to be
'powerpc:7400'. This impossibility to auto detect the architecture is
what prompted me to have Altivec defined anyway for the default
arch. [Hopefully this deficiency will be rectified soon].

The patch below includes the patch previously submitted:
http://sources.redhat.com/ml/gdb-patches/2001-11/msg00447.html

The first Changelog is just for the most recent change set. I included
the older one for easy reference. I would like to commit these in 2
separate passes, the same way they were submitted.

I tested this on AIX4.3, on a Linux-ppc with Altivec kernel support,
and on a Linux-ppx w/o Altivec kernel support.

Elena


2001-11-28  Elena Zannoni  <ezannoni@redhat.com>

	* Makefile.in (rs6000-tdep.o): Add dependency on parser-defs.h.

	* config/powerpc/nm-linux.h (FETCH_INFERIOR_REGISTERS): Define.

	* rs6000-tdep.c: Include parser-defs.h.
        (struct gdbarch_tdep): Moved from here to ppc-tdep.h.
	(rs6000_register_virtual_type): Rewrite, including hangling of
	AltiVec regs type.
	(altivec_register_p): New function.
	(rs6000_do_altivec_registers): New function.
	(rs6000_altivec_registers_info): New function.
	(rs6000_do_registers_info): New function.
	(R16): Define.
	(PPC_ALTIVEC_REGS): Define.
	(registers_powerpc): Add AltiVec registers.
	(registers_7400): Define.
	(variants): Add 7400 machine.
	(rs6000_gdbarch_init): Set the numbers of AltiVec registers.
	Initialize gdbarch_do)_registers_info.
	(rs6000_info_power_command): New function.
	(info_power_cmdlist): New static variable.
	(_initialize_rs6000_tdep): Add new 'info power altivec' command.

	* rs6000-nat.c (fetch_register): Don't error out unless the
	register number is really bogus.

	* ppc-tdep.h (struct gdbarch_tdep): Moved here from rs6000-tdep.c.
	Add altivec regnum fields.
	(altivec_register_p): Export.

	* ppc-linux-nat.c (ppc_ptrace_cannot_fetch_store_register): New
	function.
	(ppc_register_u_addr): Add handling of AltiVec registers.
	(fetch_register): New function.
	(fetch_altivec_register): New function.
	(fetch_ppc_registers): New function.
	(fetch_altivec_registers): New function.
	(fetch_inferior_registers): New function.
	(store_register): New function.
	(store_altivec_register): New function.
	(store_ppc_registers): New function.
	(store_altivec_registers): New function.
	(store_inferior_registers): New function.

[PREVIOUS CHANGELOG]
2001-11-26  Elena Zannoni  <ezannoni@redhat.com>

	* Makefile.in (ppc-linux-nat.o): Add dependency on ppc-tdep.h.

	* ppc-tdep.h (PPC_GPLAST_REGNUM): Define.

	* ppc-linux-nat.c: Include ppc-tdep.h.
	(ppc_register_u_addr): Don't use the static array regmap[],
	dynamically define the mapping instead.
	(supply_gregset): Ditto.
	(fill_gregset): Ditto.
	(COPY_REG): Delete macro defintion.
	(regmap): Delete array.
	


Index: Makefile.in
===================================================================
RCS file: /cvs/uberbaum/gdb/Makefile.in,v
retrieving revision 1.130
diff -u -p -r1.130 Makefile.in
--- Makefile.in	2001/11/18 05:09:26	1.130
+++ Makefile.in	2001/11/28 23:26:05
@@ -1818,7 +1818,7 @@ ppc-bdm.o: ppc-bdm.c $(defs_h) $(gdbcore
 	$(objfiles_h) $(gdb_stabs_h) $(serial_h) ocd.h $(regcache_h)
 
 ppc-linux-nat.o: ppc-linux-nat.c $(defs_h) $(gdbcore_h) $(frame_h) \
-	$(inferior_h) $(target_h) $(regcache_h)
+	$(inferior_h) $(target_h) $(regcache_h) ppc-tdep.h
 
 ppc-linux-tdep.o: ppc-linux-tdep.c $(defs_h) $(gdbcore_h) $(inferior_h) \
 	$(target_h) ppc-tdep.h $(regcache_h) $(value_h)
@@ -1972,7 +1972,7 @@ rs6000-nat.o: rs6000-nat.c $(bfd_h) $(de
 	$(gdb_stabs_h) $(regcache_h)
 
 rs6000-tdep.o: rs6000-tdep.c $(defs_h) $(gdbcore_h) $(inferior_h) \
-	$(target_h) ppc-tdep.h $(regcache_h) $(value_h)
+	$(target_h) ppc-tdep.h $(regcache_h) $(value_h) $(parser_defs_h)
 
 s390-tdep.o: s390-tdep.c $(defs_h) $(arch_utils_h) $(frame_h) $(inferior_h) \
 	$(symtab_h) $(target_h) $(gdbcore_h) $(gdbcmd_h) $(symfile_h) \
Index: ppc-linux-nat.c
===================================================================
RCS file: /cvs/uberbaum/gdb/ppc-linux-nat.c,v
retrieving revision 1.10
diff -u -p -r1.10 ppc-linux-nat.c
--- ppc-linux-nat.c	2001/11/20 16:35:24	1.10
+++ ppc-linux-nat.c	2001/11/28 23:26:16
@@ -32,34 +32,341 @@
 #include <sys/wait.h>
 #include <fcntl.h>
 #include <sys/procfs.h>
+#include <sys/ptrace.h>
 
 /* Prototypes for supply_gregset etc. */
 #include "gregset.h"
+#include "ppc-tdep.h"
 
+#ifndef PT_READ_U
+#define PT_READ_U PTRACE_PEEKUSR
+#endif
+#ifndef PT_WRITE_U
+#define PT_WRITE_U PTRACE_POKEUSR
+#endif
+
+/* Default the type of the ptrace transfer to int.  */
+#ifndef PTRACE_XFER_TYPE
+#define PTRACE_XFER_TYPE int
+#endif
+
+/* AltiVec regs are not always supported by the kernel. We need to fake them,
+   and let the ptrace requests fail.  */
+#ifndef PT_VR0
+#define PT_VR0 (PT_FPSCR + 1)
+#endif
+#ifndef PT_VR31
+#define PT_VR31 (PT_VR0 + 4*31)
+#endif
+#ifndef PT_VRCR
+#define PT_VRCR (PT_VR0 + 4*32)
+#endif
+#ifndef PT_VRSAVE
+#define PT_VRSAVE (PT_VR0 + 4*33)
+#endif
+
 int
 kernel_u_size (void)
 {
   return (sizeof (struct user));
 }
 
-static int regmap[] =
-{PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
- PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_R13, PT_R14, PT_R15,
- PT_R16, PT_R17, PT_R18, PT_R19, PT_R20, PT_R21, PT_R22, PT_R23,
- PT_R24, PT_R25, PT_R26, PT_R27, PT_R28, PT_R29, PT_R30, PT_R31,
- PT_FPR0, PT_FPR0 + 2, PT_FPR0 + 4, PT_FPR0 + 6, PT_FPR0 + 8, PT_FPR0 + 10, PT_FPR0 + 12, PT_FPR0 + 14,
- PT_FPR0 + 16, PT_FPR0 + 18, PT_FPR0 + 20, PT_FPR0 + 22, PT_FPR0 + 24, PT_FPR0 + 26, PT_FPR0 + 28, PT_FPR0 + 30,
- PT_FPR0 + 32, PT_FPR0 + 34, PT_FPR0 + 36, PT_FPR0 + 38, PT_FPR0 + 40, PT_FPR0 + 42, PT_FPR0 + 44, PT_FPR0 + 46,
- PT_FPR0 + 48, PT_FPR0 + 50, PT_FPR0 + 52, PT_FPR0 + 54, PT_FPR0 + 56, PT_FPR0 + 58, PT_FPR0 + 60, PT_FPR0 + 62,
- PT_NIP, PT_MSR, PT_CCR, PT_LNK, PT_CTR, PT_XER, PT_MQ};
+/* *INDENT-OFF* */
+/* registers layout, as presented by the ptrace interface:
+PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
+PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_R13, PT_R14, PT_R15,
+PT_R16, PT_R17, PT_R18, PT_R19, PT_R20, PT_R21, PT_R22, PT_R23,
+PT_R24, PT_R25, PT_R26, PT_R27, PT_R28, PT_R29, PT_R30, PT_R31,
+PT_FPR0, PT_FPR0 + 2, PT_FPR0 + 4, PT_FPR0 + 6, PT_FPR0 + 8, PT_FPR0 + 10, PT_FPR0 + 12, PT_FPR0 + 14,
+PT_FPR0 + 16, PT_FPR0 + 18, PT_FPR0 + 20, PT_FPR0 + 22, PT_FPR0 + 24, PT_FPR0 + 26, PT_FPR0 + 28, PT_FPR0 + 30,
+PT_FPR0 + 32, PT_FPR0 + 34, PT_FPR0 + 36, PT_FPR0 + 38, PT_FPR0 + 40, PT_FPR0 + 42, PT_FPR0 + 44, PT_FPR0 + 46,
+PT_FPR0 + 48, PT_FPR0 + 50, PT_FPR0 + 52, PT_FPR0 + 54, PT_FPR0 + 56, PT_FPR0 + 58, PT_FPR0 + 60, PT_FPR0 + 62,
+PT_NIP, PT_MSR, PT_CCR, PT_LNK, PT_CTR, PT_XER, PT_MQ */
+/* *INDENT_ON * */
 
 int 
-ppc_register_u_addr (int ustart, int regnum)
+ppc_register_u_addr (int ustart, int regno)
+{
+  int u_addr = -1;
+
+  /* General purpose registers occupy 1 slot each in the buffer */
+  if (regno >= PPC_GP0_REGNUM && regno <= PPC_GPLAST_REGNUM )
+    u_addr =  (ustart + (PT_R0 + regno) * 4);
+
+  /* Floating point regs: 2 slots each */
+  if (regno >= FP0_REGNUM && regno <= FPLAST_REGNUM)
+    u_addr = (ustart + (PT_FPR0 + (regno - FP0_REGNUM) * 2) * 4);
+
+  /* Altivec registers: 4 slots each. */
+  if (altivec_register_p (regno))
+    u_addr = (ustart + (PT_VR0 + (regno - gdbarch_tdep (current_gdbarch)->first_altivec_regnum) * 4) * 4);
+
+  /* UISA special purpose registers: 1 slot each */
+  if (regno == PC_REGNUM)
+    u_addr = ustart + PT_NIP * 4;
+  if (regno == PPC_LR_REGNUM)
+    u_addr = ustart + PT_LNK * 4;
+  if (regno == PPC_CR_REGNUM)
+    u_addr = ustart + PT_CCR * 4;
+  if (regno == PPC_XER_REGNUM)
+    u_addr = ustart + PT_XER * 4;
+  if (regno == PPC_CTR_REGNUM)
+    u_addr = ustart + PT_CTR * 4;
+  if (regno == PPC_MQ_REGNUM)
+    u_addr = ustart + PT_MQ * 4;
+  if (regno == PPC_PS_REGNUM)
+    u_addr = ustart + PT_MSR * 4;
+
+  return u_addr;
+}
+
+static int
+ppc_ptrace_cannot_fetch_store_register (int regno)
+{
+  return (ppc_register_u_addr (0, regno) == -1);
+}
+
+static void
+fetch_register (int regno)
+{
+  /* This isn't really an address.  But ptrace thinks of it as one.  */
+  CORE_ADDR regaddr;
+  char mess[128];              /* For messages */
+  register int i;
+  unsigned int offset;         /* Offset of registers within the u area. */
+  char buf[MAX_REGISTER_RAW_SIZE];
+  int tid;
+
+  if (ppc_ptrace_cannot_fetch_store_register (regno))
+    {
+      memset (buf, '\0', REGISTER_RAW_SIZE (regno));   /* Supply zeroes */
+      supply_register (regno, buf);
+      return;
+    }
+
+  /* Overload thread id onto process id */
+  if ((tid = TIDGET (inferior_ptid)) == 0)
+    tid = PIDGET (inferior_ptid);      /* no thread id, just use process id */
+
+  offset = U_REGS_OFFSET;
+
+  regaddr = register_addr (regno, offset);
+  if (regaddr < 0)
+    return;
+  for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
+    {
+      errno = 0;
+      *(PTRACE_XFER_TYPE *) & buf[i] = ptrace (PT_READ_U, tid,
+					       (PTRACE_ARG3_TYPE) regaddr, 0);
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+      if (errno != 0)
+	{
+	  sprintf (mess, "reading register %s (#%d)", 
+		   REGISTER_NAME (regno), regno);
+	  perror_with_name (mess);
+	}
+    }
+  supply_register (regno, buf);
+}
+
+static void
+fetch_altivec_register (int regno)
+{
+  /* This isn't really an address.  But ptrace thinks of it as one.  */
+  CORE_ADDR regaddr;
+  int i;
+  unsigned int offset;         /* Offset of registers within the u area.  */
+  char buf[MAX_REGISTER_RAW_SIZE];
+  int tid;
+
+  if (ppc_ptrace_cannot_fetch_store_register (regno))
+    {
+      memset (buf, '\0', REGISTER_RAW_SIZE (regno));   /* Supply zeroes */
+      supply_register (regno, buf);
+      return;
+    }
+
+  /* Overload thread id onto process id */
+  if ((tid = TIDGET (inferior_ptid)) == 0)
+    tid = PIDGET (inferior_ptid);      /* no thread id, just use process id */
+
+  offset = U_REGS_OFFSET;
+
+  regaddr = register_addr (regno, offset);
+  for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
+    {
+      errno = 0;
+      *(PTRACE_XFER_TYPE *) & buf[i] = ptrace (PT_READ_U, tid,
+					       (PTRACE_ARG3_TYPE) regaddr, 0);
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+      if (errno != 0)
+	{
+	  memset (buf, '\0', REGISTER_RAW_SIZE (regno));        /* Supply zeroes */
+	  supply_register (regno, buf);   
+	  return;
+	}
+    }
+  supply_register (regno, buf);
+}
+
+static void 
+fetch_ppc_registers (void)
+{
+  int last_register;
+  int i;
+
+  last_register = PPC_MQ_REGNUM;
+  for (i = 0; i <= last_register; i++)
+    fetch_register (i);
+}
+
+static void 
+fetch_altivec_registers (void)
+{
+  int i;
+
+  for (i = 0;
+       i < gdbarch_tdep (current_gdbarch)->last_altivec_regnum
+	 - gdbarch_tdep (current_gdbarch)->first_altivec_regnum;
+       i++)
+    fetch_altivec_register (gdbarch_tdep (current_gdbarch)->first_altivec_regnum + i);
+}
+
+/* Fetch registers from the child process.  Fetch all registers if
+   regno == -1, otherwise fetch all general registers or all floating
+   point registers depending upon the value of regno.  */
+
+void
+fetch_inferior_registers (int regno)
+{
+  if (regno == -1)
+    {
+      fetch_ppc_registers ();
+      fetch_altivec_registers ();
+    }
+  else 
+    {
+      if (altivec_register_p (regno))
+	fetch_altivec_register (regno);
+      else
+	fetch_register (regno);
+    }
+}
+
+/* Store one register. */
+static void
+store_register (int regno)
+{
+  /* This isn't really an address.  But ptrace thinks of it as one.  */
+  CORE_ADDR regaddr;
+  char mess[128];              /* For messages */
+  register int i;
+  unsigned int offset;         /* Offset of registers within the u area.  */
+  int tid;
+  char *buf = alloca (MAX_REGISTER_RAW_SIZE);
+
+  if (ppc_ptrace_cannot_fetch_store_register (regno))
+    {
+      return;
+    }
+
+  /* Overload thread id onto process id */
+  if ((tid = TIDGET (inferior_ptid)) == 0)
+    tid = PIDGET (inferior_ptid);      /* no thread id, just use process id */
+
+  offset = U_REGS_OFFSET;
+
+  regaddr = register_addr (regno, offset);
+  regcache_collect (regno, buf);
+  for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
+    {
+      errno = 0;
+      ptrace (PT_WRITE_U, tid, (PTRACE_ARG3_TYPE) regaddr,
+	      *(PTRACE_XFER_TYPE *) & buf[i]);
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+      if (errno != 0)
+	{
+	  sprintf (mess, "writing register %s (#%d)", 
+		   REGISTER_NAME (regno), regno);
+	  perror_with_name (mess);
+	}
+    }
+}
+
+static void
+store_altivec_register (int regno)
+{
+  /* This isn't really an address.  But ptrace thinks of it as one.  */
+  CORE_ADDR regaddr;
+  char mess[128];               /* For messages */
+  register int i;
+  unsigned int offset;          /* Offset of registers within the u area.  */
+  int tid;
+  char *buf = alloca (MAX_REGISTER_RAW_SIZE);
+
+  if (ppc_ptrace_cannot_fetch_store_register (regno))
+    return;
+
+  /* Overload thread id onto process id */
+  if ((tid = TIDGET (inferior_ptid)) == 0)
+    tid = PIDGET (inferior_ptid);       /* no thread id, just use process id */
+
+  offset = U_REGS_OFFSET;
+
+  regaddr = register_addr (regno, offset);
+  regcache_collect (regno, buf);
+  for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
+    {
+      errno = 0;
+      ptrace (PT_WRITE_U, tid, (PTRACE_ARG3_TYPE) regaddr,
+              *(PTRACE_XFER_TYPE *) & buf[i]);
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+      if (errno != 0)
+        return;
+    }
+}
+
+static void
+store_ppc_registers (void)
+{
+  int last_register;
+  int i;
+
+  last_register = PPC_MQ_REGNUM;
+  for (i = 0; i <= last_register; i++)
+    store_register (i);
+}
+
+static void
+store_altivec_registers (void)
 {
-  return (ustart + 4 * regmap[regnum]);
+  int i;
+
+  for (i = 0; 
+       i < gdbarch_tdep (current_gdbarch)->last_altivec_regnum 
+	 - gdbarch_tdep (current_gdbarch)->first_altivec_regnum; 
+       i++)
+    store_altivec_register (gdbarch_tdep (current_gdbarch)->first_altivec_regnum + i);
 }
 
 void
+store_inferior_registers (int regno)
+{
+  if (regno >= 0)
+    {
+      if (altivec_register_p (regno))
+        store_altivec_register (regno);
+      else
+        store_register (regno);
+    }
+  else
+    {
+      store_ppc_registers ();
+      store_altivec_registers ();
+    }
+}
+
+void
 supply_gregset (gdb_gregset_t *gregsetp)
 {
   int regi;
@@ -68,8 +375,13 @@ supply_gregset (gdb_gregset_t *gregsetp)
   for (regi = 0; regi < 32; regi++)
     supply_register (regi, (char *) (regp + regi));
 
-  for (regi = FIRST_UISA_SP_REGNUM; regi <= LAST_UISA_SP_REGNUM; regi++)
-    supply_register (regi, (char *) (regp + regmap[regi]));
+  supply_register (PC_REGNUM, (char *) (regp + PT_NIP));
+  supply_register (PPC_LR_REGNUM, (char *) (regp + PT_LNK));
+  supply_register (PPC_CR_REGNUM, (char *) (regp + PT_CCR));
+  supply_register (PPC_XER_REGNUM, (char *) (regp + PT_XER));
+  supply_register (PPC_CTR_REGNUM, (char *) (regp + PT_CTR));
+  supply_register (PPC_MQ_REGNUM, (char *) (regp + PT_MQ));
+  supply_register (PPC_PS_REGNUM, (char *) (regp + PT_MSR));
 }
 
 void
@@ -78,19 +390,26 @@ fill_gregset (gdb_gregset_t *gregsetp, i
   int regi;
   elf_greg_t *regp = (elf_greg_t *) gregsetp;
 
-#define COPY_REG(_idx_,_regi_) \
-  if ((regno == -1) || regno == _regi_) \
-    regcache_collect (_regi_, regp + _idx_)
-
   for (regi = 0; regi < 32; regi++)
     {
-      COPY_REG (regmap[regi], regi);
+      if ((regno == -1) || regno == regi)
+        regcache_collect (regi, regp + PT_R0 + regi);
     }
 
-  for (regi = FIRST_UISA_SP_REGNUM; regi <= LAST_UISA_SP_REGNUM; regi++)
-    {
-      COPY_REG (regmap[regi], regi);
-    }
+  if ((regno == -1) || regno == PC_REGNUM)
+    regcache_collect (PC_REGNUM, regp + PT_NIP);
+  if ((regno == -1) || regno == PPC_LR_REGNUM)
+    regcache_collect (PPC_LR_REGNUM, regp + PT_LNK);
+  if ((regno == -1) || regno == PPC_CR_REGNUM)
+    regcache_collect (PPC_CR_REGNUM, regp + PT_CCR);
+  if ((regno == -1) || regno == PPC_XER_REGNUM)
+    regcache_collect (PPC_XER_REGNUM, regp + PT_XER);
+  if ((regno == -1) || regno == PPC_CTR_REGNUM)
+    regcache_collect (PPC_CTR_REGNUM, regp + PT_CTR);
+  if ((regno == -1) || regno == PPC_MQ_REGNUM)
+    regcache_collect (PPC_MQ_REGNUM, regp + PT_MQ);
+  if ((regno == -1) || regno == PPC_PS_REGNUM)
+    regcache_collect (PPC_PS_REGNUM, regp + PT_MSR);
 }
 
 void
Index: ppc-tdep.h
===================================================================
RCS file: /cvs/uberbaum/gdb/ppc-tdep.h,v
retrieving revision 1.2
diff -u -p -r1.2 ppc-tdep.h
--- ppc-tdep.h	2001/11/01 01:07:35	1.2
+++ ppc-tdep.h	2001/11/29 00:18:11
@@ -42,10 +42,24 @@ void rs6000_init_extra_frame_info (int f
 int rs6000_frameless_function_invocation (struct frame_info *);
 void rs6000_frame_init_saved_regs (struct frame_info *);
 CORE_ADDR rs6000_frame_chain (struct frame_info *);
+int altivec_register_p (int regno);
 
+/* Private data that this module attaches to struct gdbarch. */
+
+struct gdbarch_tdep
+  {
+    int wordsize;              /* size in bytes of fixed-point word */
+    int osabi;                 /* OS / ABI from ELF header */
+    int *regoff;               /* byte offsets in register arrays */
+    const struct reg *regs;    /* from current variant */
+    int first_altivec_regnum;
+    int last_altivec_regnum;
+  };
+
 /* Some important register numbers. */
 
 #define	PPC_GP0_REGNUM 0		/* GPR register 0 */
+#define	PPC_GPLAST_REGNUM 31		/* GPR register 31 */
 #define	PPC_TOC_REGNUM 2		/* TOC register */
 #define PPC_PS_REGNUM 65		/* Processor (or machine) status (%msr) */
 #define	PPC_CR_REGNUM 66		/* Condition register */
Index: rs6000-nat.c
===================================================================
RCS file: /cvs/uberbaum/gdb/rs6000-nat.c,v
retrieving revision 1.17
diff -u -p -r1.17 rs6000-nat.c
--- rs6000-nat.c	2001/05/04 04:15:27	1.17
+++ rs6000-nat.c	2001/11/28 23:26:17
@@ -206,9 +206,12 @@ fetch_register (int regno)
 
   /* Bogus register number. */
   else if (regno > LAST_UISA_SP_REGNUM)
-    fprintf_unfiltered (gdb_stderr,
-			"gdb error: register no %d not implemented.\n",
-			regno);
+    {
+      if (regno >= NUM_REGS)
+	fprintf_unfiltered (gdb_stderr,
+			    "gdb error: register no %d not implemented.\n",
+			    regno);
+    }
 
   /* Fixed-point registers. */
   else
Index: rs6000-tdep.c
===================================================================
RCS file: /cvs/uberbaum/gdb/rs6000-tdep.c,v
retrieving revision 1.28
diff -u -p -r1.28 rs6000-tdep.c
--- rs6000-tdep.c	2001/10/21 17:19:37	1.28
+++ rs6000-tdep.c	2001/11/28 23:26:19
@@ -33,6 +33,7 @@
 #include "regcache.h"
 #include "doublest.h"
 #include "value.h"
+#include "parser-defs.h"
 
 #include "bfd/libbfd.h"		/* for bfd_default_set_arch_mach */
 #include "coff/internal.h"	/* for libcoff.h */
@@ -81,16 +82,6 @@ struct reg
     unsigned char fpr;		/* whether register is floating-point */
   };
 
-/* Private data that this module attaches to struct gdbarch. */
-
-struct gdbarch_tdep
-  {
-    int wordsize;		/* size in bytes of fixed-point word */
-    int osabi;			/* OS / ABI from ELF header */
-    int *regoff;		/* byte offsets in register arrays */
-    const struct reg *regs;	/* from current variant */
-  };
-
 /* Return the current architecture's gdbarch_tdep structure. */
 
 #define TDEP	gdbarch_tdep (current_gdbarch)
@@ -1552,9 +1543,24 @@ rs6000_register_virtual_type (int n)
   struct gdbarch_tdep *tdep = TDEP;
   const struct reg *reg = tdep->regs + n;
 
-  return reg->fpr ? builtin_type_double :
-    regsize (reg, tdep->wordsize) == 8 ? builtin_type_int64 :
-      builtin_type_int32;
+  if (reg->fpr)
+    return builtin_type_double;
+  else
+    {
+      int size = regsize (reg, tdep->wordsize);
+      switch (size)
+	{
+	case 8:
+	  return builtin_type_int64;
+	  break;
+	case 16:
+	  return builtin_type_int128;
+	  break;
+	default:
+	  return builtin_type_int32;
+	  break;
+	}
+    }
 }
 
 /* For the PowerPC, it appears that the debug info marks float parameters as
@@ -1613,6 +1619,192 @@ rs6000_register_convert_to_raw (struct t
     memcpy (to, from, REGISTER_RAW_SIZE (n));
 }
 
+int
+altivec_register_p (int regno)
+{
+  if (gdbarch_tdep (current_gdbarch)->first_altivec_regnum < 0
+      || gdbarch_tdep (current_gdbarch)->last_altivec_regnum < 0)
+    return 0;
+  else
+    return (regno >= gdbarch_tdep (current_gdbarch)->first_altivec_regnum
+	    && regno <= gdbarch_tdep (current_gdbarch)->last_altivec_regnum);
+}
+
+static void
+rs6000_do_altivec_registers (int regnum)
+{
+  int i;
+  char *raw_buffer = (char*) alloca (MAX_REGISTER_RAW_SIZE);
+  char *virtual_buffer = (char*) alloca (MAX_REGISTER_VIRTUAL_SIZE);
+
+  for (i = gdbarch_tdep (current_gdbarch)->first_altivec_regnum;
+       i <= gdbarch_tdep (current_gdbarch)->last_altivec_regnum; i++)
+    {
+      /* If we want just one reg, check that this is the one we want. */
+      if (regnum != -1 && i != regnum)
+	continue;
+
+      /* If the register name is empty, it is undefined for this
+         processor, so don't display anything.  */
+      if (REGISTER_NAME (i) == NULL || *(REGISTER_NAME (i)) == '\0')
+        continue;
+
+      fputs_filtered (REGISTER_NAME (i), gdb_stdout);
+      print_spaces_filtered (15 - strlen (REGISTER_NAME (i)), gdb_stdout);
+
+      /* Get the data in raw format.  */
+      if (read_relative_register_raw_bytes (i, raw_buffer))
+        {
+          printf_filtered ("*value not available*\n");
+          continue;
+        }
+
+      /* Convert raw data to virtual format if necessary.  */
+      if (REGISTER_CONVERTIBLE (i))
+	REGISTER_CONVERT_TO_VIRTUAL (i, REGISTER_VIRTUAL_TYPE (i),
+				     raw_buffer, virtual_buffer);
+      else
+	memcpy (virtual_buffer, raw_buffer, REGISTER_VIRTUAL_SIZE (i));
+
+      /* Print as integer in hex only.  */
+      val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, 0,
+                 gdb_stdout, 'x', 1, 0, Val_pretty_default);
+      printf_filtered ("\n");
+    }
+}
+
+static void
+rs6000_altivec_registers_info (char *addr_exp, int from_tty)
+{
+  int regnum, numregs;
+  register char *end;
+
+  if (!target_has_registers)
+    error ("The program has no registers now.");
+  if (selected_frame == NULL)
+    error ("No selected frame.");
+
+  if (!addr_exp)
+    {
+      rs6000_do_altivec_registers (-1);
+      return;
+    }
+
+  numregs = NUM_REGS + NUM_PSEUDO_REGS;
+  do
+    {
+      if (addr_exp[0] == '$')
+	addr_exp++;
+      end = addr_exp;
+      while (*end != '\0' && *end != ' ' && *end != '\t')
+	++end;
+
+      regnum = target_map_name_to_register (addr_exp, end - addr_exp);
+      if (regnum < 0)
+        {
+          regnum = numregs;
+          if (*addr_exp >= '0' && *addr_exp <= '9')
+	    regnum = atoi (addr_exp);	/* Take a number */
+          if (regnum >= numregs)	/* Bad name, or bad number */
+	    error ("%.*s: invalid register", end - addr_exp, addr_exp);
+	}
+
+      rs6000_do_altivec_registers (regnum);
+
+      addr_exp = end;
+      while (*addr_exp == ' ' || *addr_exp == '\t')
+	++addr_exp;
+    }
+  while (*addr_exp != '\0');
+}
+
+static void
+rs6000_do_registers_info (int regnum, int fpregs)
+{
+  register int i;
+  int numregs = NUM_REGS + NUM_PSEUDO_REGS;
+  char *raw_buffer = (char*) alloca (MAX_REGISTER_RAW_SIZE);
+  char *virtual_buffer = (char*) alloca (MAX_REGISTER_VIRTUAL_SIZE);
+
+  for (i = 0; i < numregs; i++)
+    {
+      /* Decide between printing all regs, nonfloat regs, or specific reg.  */
+      if (regnum == -1)
+        {
+          if ((TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT && !fpregs)
+	      || (altivec_register_p (i) && !fpregs))
+            continue;
+        }
+      else
+        {
+          if (i != regnum)
+            continue;
+        }
+
+      /* If the register name is empty, it is undefined for this
+         processor, so don't display anything.  */
+      if (REGISTER_NAME (i) == NULL || *(REGISTER_NAME (i)) == '\0')
+        continue;
+
+      fputs_filtered (REGISTER_NAME (i), gdb_stdout);
+      print_spaces_filtered (15 - strlen (REGISTER_NAME (i)), gdb_stdout);
+
+      /* Get the data in raw format.  */
+      if (read_relative_register_raw_bytes (i, raw_buffer))
+        {
+          printf_filtered ("*value not available*\n");
+          continue;
+        }
+
+      /* Convert raw data to virtual format if necessary.  */
+      if (REGISTER_CONVERTIBLE (i))
+        REGISTER_CONVERT_TO_VIRTUAL (i, REGISTER_VIRTUAL_TYPE (i),
+				     raw_buffer, virtual_buffer);
+      else
+	memcpy (virtual_buffer, raw_buffer, REGISTER_VIRTUAL_SIZE (i));
+
+      /* If virtual format is floating, print it that way, and in raw hex.  */
+      if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT)
+        {
+          register int j;
+
+#ifdef INVALID_FLOAT
+          if (INVALID_FLOAT (virtual_buffer, REGISTER_VIRTUAL_SIZE (i)))
+            printf_filtered ("<invalid float>");
+          else
+#endif
+            val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, 0,
+                       gdb_stdout, 0, 1, 0, Val_pretty_default);
+
+          printf_filtered ("\t(raw 0x");
+          for (j = 0; j < REGISTER_RAW_SIZE (i); j++)
+            {
+              register int idx = TARGET_BYTE_ORDER == BIG_ENDIAN ? j
+		: REGISTER_RAW_SIZE (i) - 1 - j;
+              printf_filtered ("%02x", (unsigned char) raw_buffer[idx]);
+            }
+          printf_filtered (")");
+        }
+      else
+	{
+	  /* Print as integer in hex and in decimal.  */
+	  if (!altivec_register_p (i))
+	    {
+	      val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, 0,
+			 gdb_stdout, 'x', 1, 0, Val_pretty_default);
+	      printf_filtered ("\t");
+	      val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, 0,
+			 gdb_stdout, 0, 1, 0, Val_pretty_default);
+	    }
+	  else
+	    /* Print as integer in hex only.  */
+	    val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, 0,
+		       gdb_stdout, 'x', 1, 0, Val_pretty_default);
+	}
+      printf_filtered ("\n");
+    }
+}
+
 /* Store the address of the place in which to copy the structure the
    subroutine will return.  This is called from call_function.
 
@@ -1752,6 +1944,10 @@ rs6000_convert_from_func_ptr_addr (CORE_
    systems. */
 #define R8(name)	{ STR(name), 8, 8, 0 }
 
+/* Return a struct reg defining register NAME that's 128 bits on all
+   systems. */
+#define R16(name)       { STR(name), 16, 16, 0 }
+
 /* Return a struct reg defining floating-point register NAME. */
 #define F(name)		{ STR(name), 8, 8, 1 }
 
@@ -1802,6 +1998,14 @@ rs6000_convert_from_func_ptr_addr (CORE_
   /* 112 */ R(srr0),   R(srr1),   R(tbl),    R(tbu),    \
   /* 116 */ R4(dec),   R(dabr),   R4(ear)
 
+/* AltiVec registers */
+#define PPC_ALTIVEC_REGS \
+  /*119*/R16(vr0), R16(vr1), R16(vr2), R16(vr3), R16(vr4), R16(vr5), R16(vr6), R16(vr7),  \
+  /*127*/R16(vr8), R16(vr9), R16(vr10),R16(vr11),R16(vr12),R16(vr13),R16(vr14),R16(vr15), \
+  /*135*/R16(vr16),R16(vr17),R16(vr18),R16(vr19),R16(vr20),R16(vr21),R16(vr22),R16(vr23), \
+  /*143*/R16(vr24),R16(vr25),R16(vr26),R16(vr27),R16(vr28),R16(vr29),R16(vr30),R16(vr31), \
+  /*151*/R4(vscr), R4(vrsave)
+
 /* IBM POWER (pre-PowerPC) architecture, user-level view.  We only cover
    user-level SPR's. */
 static const struct reg registers_power[] =
@@ -1815,7 +2019,8 @@ static const struct reg registers_power[
 static const struct reg registers_powerpc[] =
 {
   COMMON_UISA_REGS,
-  PPC_UISA_SPRS
+  PPC_UISA_SPRS,
+  PPC_ALTIVEC_REGS
 };
 
 /* IBM PowerPC 403. */
@@ -1946,6 +2151,21 @@ static const struct reg registers_750[] 
 };
 
 
+/* Motorola PowerPC 7400. */
+static const struct reg registers_7400[] =
+{
+  /* gpr0-gpr31, fpr0-fpr31 */
+  COMMON_UISA_REGS,
+  /* ctr, xre, lr, cr */
+  PPC_UISA_SPRS,
+  /* sr0-sr15 */
+  PPC_SEGMENT_REGS,
+  PPC_OEA_SPRS,
+  /* vr0-vr31, vrsave, vscr */
+  PPC_ALTIVEC_REGS
+  /* FIXME? Add more registers? */
+};
+
 /* Information about a particular processor variant.  */
 
 struct variant
@@ -2005,6 +2225,8 @@ static const struct variant variants[] =
    bfd_mach_ppc_860, num_registers (registers_860), registers_860},
   {"750", "Motorola/IBM PowerPC 750 or 740", bfd_arch_powerpc,
    bfd_mach_ppc_750, num_registers (registers_750), registers_750},
+  {"7400", "Motorola/IBM PowerPC 7400 (G4)", bfd_arch_powerpc,
+   bfd_mach_ppc_7400, num_registers (registers_7400), registers_7400},
 
   /* FIXME: I haven't checked the register sets of the following. */
   {"620", "Motorola PowerPC 620", bfd_arch_powerpc,
@@ -2235,6 +2457,23 @@ rs6000_gdbarch_init (struct gdbarch_info
     v = find_variant_by_name (power ? "power" : "powerpc");
   tdep->regs = v->regs;
 
+  if (v->arch == bfd_arch_powerpc)
+    switch (v->mach)
+      {
+      case bfd_mach_ppc: 
+	tdep->first_altivec_regnum = 71;
+	tdep->last_altivec_regnum = 104;
+	break;
+      case bfd_mach_ppc_7400:
+	tdep->first_altivec_regnum = 119;
+	tdep->last_altivec_regnum = 153;
+	break;
+      default:
+	tdep->first_altivec_regnum = -1;
+	tdep->last_altivec_regnum = -1;
+	break;
+      }   
+
   /* Calculate byte offsets in raw register array. */
   tdep->regoff = xmalloc (v->nregs * sizeof (int));
   for (i = off = 0; i < v->nregs; i++)
@@ -2263,6 +2502,7 @@ rs6000_gdbarch_init (struct gdbarch_info
   set_gdbarch_register_virtual_size (gdbarch, rs6000_register_virtual_size);
   set_gdbarch_max_register_virtual_size (gdbarch, 8);
   set_gdbarch_register_virtual_type (gdbarch, rs6000_register_virtual_type);
+  set_gdbarch_do_registers_info (gdbarch, rs6000_do_registers_info);
 
   set_gdbarch_ptr_bit (gdbarch, wordsize * TARGET_CHAR_BIT);
   set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT);
@@ -2359,6 +2599,14 @@ rs6000_gdbarch_init (struct gdbarch_info
   return gdbarch;
 }
 
+static struct cmd_list_element *info_power_cmdlist = NULL;
+
+static void
+rs6000_info_power_command (char *args, int from_tty)
+{
+  help_list (info_power_cmdlist, "info power ", class_info, gdb_stdout);
+}
+
 /* Initialization code.  */
 
 void
@@ -2366,4 +2614,14 @@ _initialize_rs6000_tdep (void)
 {
   register_gdbarch_init (bfd_arch_rs6000, rs6000_gdbarch_init);
   register_gdbarch_init (bfd_arch_powerpc, rs6000_gdbarch_init);
+
+  /* Add root prefix command for "info power" commands */
+  add_prefix_cmd ("power", class_info, rs6000_info_power_command,
+		  "Various POWERPC info specific commands.",
+		  &info_power_cmdlist, "info power ", 0, &infolist);
+
+  add_cmd ("altivec", class_info, rs6000_altivec_registers_info,
+	   "Display the contents of the AltiVec registers.",
+	   &info_power_cmdlist);
+
 }
Index: config/powerpc/nm-linux.h
===================================================================
RCS file: /cvs/uberbaum/gdb/config/powerpc/nm-linux.h,v
retrieving revision 1.9
diff -u -p -r1.9 nm-linux.h
--- nm-linux.h	2001/11/20 16:36:44	1.9
+++ nm-linux.h	2001/11/28 23:26:20
@@ -30,6 +30,8 @@ extern int kernel_u_size (void);
 
 #define U_REGS_OFFSET 0
 
+#define FETCH_INFERIOR_REGISTERS
+
 extern int ppc_register_u_addr (int, int);
 #define REGISTER_U_ADDR(addr, blockend, regno) \
         (addr) = ppc_register_u_addr ((blockend),(regno));



More information about the Gdb-patches mailing list