Re: [RFA][3/5] New port: Cell BE SPU (the port itself)

Ulrich Weigand
2006-11-12T15:38:43Z

Hi Mark,

thanks for looking at the port!

> First, "spu" doesn't occur anywhere in config.guess. Is this a name the
> community agrees on? I understand it stands for Synergistic Processor Unit,
> and it seems a bad idea to me that's a fairly generic term.

"spu" cannot occur as host architecture, therefore it is not in config.guess.
It is, however, in config.sub as a target architecture, and the recently
committed binutils patch uses it (as does the proposed GCC patch and all the
previously released toolchain packages for Cell, both for Linux and the PS3).

> > * config/spu/spu-cell.mt: New file.
> > * config/spu/spu.mt: New file.
>
> Two .mt files? I think spu-cell.mt should be renamed spu.mh.

Since spu is never the host architecture, spu.mh would not get used.

The situation is a bit unique here: The spu debugger is itself a PowerPC
binary and runs on the PowerPC side of the Cell, but it debugs code running
on the SPU side, so it would be a "cross" configuration. On the other hand,
the debugger makes use of special host operating system facilities (ptrace
plus the spufs file system) to control the SPU inferior -- in this aspect
it looks like a "native" configuration.

I've tried different ways to integrate this scenario into the GDB configure
structure, and what I've come up with appeared to me to be the most straight-
forward way.

This means I leave GDB's notion of "host" as auto-detected (i.e. powerpc64),
which means that GDB does not configure as a "native" target (since target
!= host). However, the target-dependent files for the spu target actually
include the spu-linux-nat.c file which installs itself onto the target stack
and provides the "native" debugging capabilities that way.

Of course I also need the "real" cross-SPU target, e.g. to build an x86-
hosted spu cross-debugger that can attach to a gdbserver on the Cell.
To achieve this I've defined *two* target definitions, and have configure.tgt
select which of those to use depending on the host architecture.

> > * spu-nat.c: New file.
>
> This file seems to be Linux-spefic. Can you rename it to spu-linux-nat.c?

Sure, done.

> > +/* Some older glibc versions do not define this. */
> > +#ifndef __WNOTHREAD
> > +#define __WNOTHREAD 0x20000000 /* Don't wait on children of
> > other
> > + threads in this group */
> > +#endif
>
> Is this really needed? I mean, sometimes stuff like this gets added when
> we're working on getting anew platform running, but things get fixed
> before there's an "offical" release.

Since this define was added only with glibc 2.4, we've really needed this
until quite recently. However, I'm fine with removing it from the version
in the FSF repository, this should be used with current/future distributions
only, anyway.

> > + sprintf (mess, "reading PPC register #%d", regno);
>
> Can you use xsnprintf() wherever you use sprintf? The later is a very
> dangerous function, and really should not be used in new code.

Done.

> > + char buf[8];
>
> Lots of places use char where you should use gdb_byte.

I thought I went through that already, but I did indeed miss some. I hope
I've now caught them all ...

> Many ports use an enum for the register numbers, which makes debugging a
> bit easier.

Done. Doing so I noticed that I forgot to implement the latest ABI
change reserving registers 75-79 as scratch registers for call stubs.
I've fixed this now as well.

> I also think SPU_NUM_CORE_REGS is a bad name. I first thought
> this had something to do with core files.

Agreed, I've renamed it.

> May I suggest SPU_NUM_VEC_REGS.

Since the SPU ISA calls them "general-purpose registers" (and it's a bit
less to type), I'm now using SPU_NUM_GPRS. OK?

Here's the current version of the patch including the above-mentioned
changes. Together with the other four patches I'm still getting the
same testsuite results.

Bye,
Ulrich

ChangeLog:

* config/spu/spu-cell.mt: New file.
* config/spu/spu.mt: New file.
* configure.tgt: Add "spu" target_cpu and "spu*-*-*" target.
* Makefile.in (spu_tdep_h): New variable.
(ALLDEPFILES): Add spu-linux-nat.c and spu-tdep.c
(spu-linux-nat.o, spu-tdep.o): Add dependencies.
* spu-linux-nat.c: New file.
* spu-tdep.c: New file.
* spu-tdep.h: New file.

diff -urN gdb-orig/gdb/config/spu/spu-cell.mt gdb-head/gdb/config/spu/spu-cell.mt
--- gdb-orig/gdb/config/spu/spu-cell.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/spu/spu-cell.mt 2006-11-12 15:04:35.110937504 +0100
@@ -0,0 +1,7 @@
+# Target: Cell BE (PowerPC64 + SPU)
+
+# This implements a 'pseudo-native' GDB running on the
+# PPU side of the Cell BE and debugging the SPU side.
+
+TDEPFILES = spu-tdep.o spu-linux-nat.o fork-child.o inf-ptrace.o
+
diff -urN gdb-orig/gdb/config/spu/spu.mt gdb-head/gdb/config/spu/spu.mt
--- gdb-orig/gdb/config/spu/spu.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/spu/spu.mt 2006-11-12 14:57:46.712974288 +0100
@@ -0,0 +1,2 @@
+# Target: Cell BE SPU
+TDEPFILES= spu-tdep.o
diff -urN gdb-orig/gdb/configure.tgt gdb-head/gdb/configure.tgt
--- gdb-orig/gdb/configure.tgt 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/configure.tgt 2006-11-12 16:09:29.132964800 +0100
@@ -25,6 +25,7 @@
thumb*) gdb_target_cpu=arm ;;
s390*) gdb_target_cpu=s390 ;;
sh*) gdb_target_cpu=sh ;;
+spu*) gdb_target_cpu=spu ;;
strongarm*) gdb_target_cpu=arm ;;
xscale*) gdb_target_cpu=arm ;;
x86_64*) gdb_target_cpu=i386 ;;
@@ -208,6 +209,13 @@
sparc-*-*) gdb_target=sparc ;;
sparc64-*-*) gdb_target=sparc64 ;;

+spu*-*-*) gdb_target=spu
+ # Support 'pseudo-native' debugging on the Cell BE
+ if test "${gdb_host_cpu}" = "powerpc"; then
+ gdb_target=spu-cell
+ fi
+ ;;
+
xstormy16-*-*) gdb_target=xstormy16 ;;

v850*-*-elf) gdb_target=v850 ;;
diff -urN gdb-orig/gdb/Makefile.in gdb-head/gdb/Makefile.in
--- gdb-orig/gdb/Makefile.in 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/Makefile.in 2006-11-12 15:04:08.590974744 +0100
@@ -799,6 +799,7 @@
sparc64_tdep_h = sparc64-tdep.h $(sparc_tdep_h)
sparc_nat_h = sparc-nat.h
sparc_tdep_h = sparc-tdep.h
+spu_tdep_h = spu-tdep.h
srec_h = srec.h
stabsread_h = stabsread.h
stack_h = stack.h
@@ -1496,6 +1497,7 @@
sparc64-tdep.c sparc64fbsd-nat.c sparc64fbsd-tdep.c \
sparc64nbsd-nat.c sparc64nbsd-tdep.c sparc64obsd-tdep.c \
sparcnbsd-nat.c sparcnbsd-tdep.c sparcobsd-tdep.c \
+ spu-linux-nat.c spu-tdep.c \
v850-tdep.c \
vax-nat.c vax-tdep.c vaxbsd-nat.c vaxnbsd-tdep.c \
win32-nat.c \
@@ -2718,6 +2720,15 @@
$(frame_unwind_h) $(gdbcore_h) $(gdbtypes_h) $(inferior_h) \
$(symtab_h) $(objfiles_h) $(osabi_h) $(regcache_h) $(target_h) \
$(value_h) $(gdb_assert_h) $(gdb_string_h) $(sparc_tdep_h)
+spu-linux-nat.o: spu-linux-nat.c $(defs_h) $(gdbcore_h) $(gdb_string_h) \
+ $(target_h) $(inferior_h) $(inf_ptrace_h) $(regcache_h) $(symfile_h) \
+ $(gdb_wait_h) $(spu_tdep_h)
+spu-tdep.o: spu-tdep.c $(defs_h) $(arch_utils_h) $(gdbtypes_h) $(gdbcmd_h) \
+ $(gdbcore_h) $(gdb_string_h) $(gdb_assert_h) $(frame_h) \
+ $(frame_unwind_h) $(frame_base_h) $(trad_frame_h) $(symtab_h) \
+ $(symfile_h) $(value_h) $(inferior_h) $(dis_asm_h) $(objfiles_h) \
+ $(language_h) $(regcache_h) $(reggroups_h) $(floatformat_h) \
+ $(spu_tdep_h)
stabsread.o: stabsread.c $(defs_h) $(gdb_string_h) $(bfd_h) $(gdb_obstack_h) \
$(symtab_h) $(gdbtypes_h) $(expression_h) $(symfile_h) $(objfiles_h) \
$(aout_stab_gnu_h) $(libaout_h) $(aout_aout64_h) $(gdb_stabs_h) \
diff -urN gdb-orig/gdb/spu-linux-nat.c gdb-head/gdb/spu-linux-nat.c
--- gdb-orig/gdb/spu-linux-nat.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-linux-nat.c 2006-11-12 15:54:26.112962216 +0100
@@ -0,0 +1,557 @@
+/* SPU native-dependent code for GDB, the GNU debugger.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+ Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+#include "target.h"
+#include "inferior.h"
+#include "inf-ptrace.h"
+#include "regcache.h"
+#include "symfile.h"
+#include "gdb_wait.h"
+
+#include <sys/ptrace.h>
+#include <asm/ptrace.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include "spu-tdep.h"
+
+/* PPU side system calls. */
+#define INSTR_SC 0x44000002
+#define NR_spu_run 0x0116
+
+
+/* Fetch PPU register REGNO. */
+static CORE_ADDR
+fetch_ppc_register (int regno)
+{
+ PTRACE_TYPE_RET res;
+
+ int tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid);
+
+#ifndef __powerpc64__
+ /* If running as a 32-bit process on a 64-bit system, we attempt
+ to get the full 64-bit register content of the target process.
+ If the PPC special ptrace call fails, we're on a 32-bit system;
+ just fall through to the regular ptrace call in that case. */
+ {
+ gdb_byte buf[8];
+
+ errno = 0;
+ ptrace (PPC_PTRACE_PEEKUSR_3264, tid,
+ (PTRACE_TYPE_ARG3) (regno * 8), buf);
+ if (errno == 0)
+ ptrace (PPC_PTRACE_PEEKUSR_3264, tid,
+ (PTRACE_TYPE_ARG3) (regno * 8 + 4), buf + 4);
+ if (errno == 0)
+ return (CORE_ADDR) *(unsigned long long *)buf;
+ }
+#endif
+
+ errno = 0;
+ res = ptrace (PT_READ_U, tid,
+ (PTRACE_TYPE_ARG3) (regno * sizeof (PTRACE_TYPE_RET)), 0);
+ if (errno != 0)
+ {
+ char mess[128];
+ xsnprintf (mess, sizeof mess, "reading PPC register #%d", regno);
+ perror_with_name (_(mess));
+ }
+
+ return (CORE_ADDR) (unsigned long) res;
+}
+
+/* Fetch WORD from PPU memory at (aligned) MEMADDR in thread TID. */
+static int
+fetch_ppc_memory_1 (int tid, CORE_ADDR memaddr, PTRACE_TYPE_RET *word)
+{
+ errno = 0;
+
+#ifndef __powerpc64__
+ if (memaddr >> 32)
+ {
+ unsigned long long addr_8 = (unsigned long long) memaddr;
+ ptrace (PPC_PTRACE_PEEKTEXT_3264, tid, (PTRACE_TYPE_ARG3) &addr_8, word);
+ }
+ else
+#endif
+ *word = ptrace (PT_READ_I, tid, (PTRACE_TYPE_ARG3) (size_t) memaddr, 0);
+
+ return errno;
+}
+
+/* Store WORD into PPU memory at (aligned) MEMADDR in thread TID. */
+static int
+store_ppc_memory_1 (int tid, CORE_ADDR memaddr, PTRACE_TYPE_RET word)
+{
+ errno = 0;
+
+#ifndef __powerpc64__
+ if (memaddr >> 32)
+ {
+ unsigned long long addr_8 = (unsigned long long) memaddr;
+ ptrace (PPC_PTRACE_POKEDATA_3264, tid, (PTRACE_TYPE_ARG3) &addr_8, word);
+ }
+ else
+#endif
+ ptrace (PT_WRITE_D, tid, (PTRACE_TYPE_ARG3) (size_t) memaddr, word);
+
+ return errno;
+}
+
+/* Fetch LEN bytes of PPU memory at MEMADDR to MYADDR. */
+static int
+fetch_ppc_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+ int i, ret;
+
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_TYPE_RET);
+ int count = ((((memaddr + len) - addr) + sizeof (PTRACE_TYPE_RET) - 1)
+ / sizeof (PTRACE_TYPE_RET));
+ PTRACE_TYPE_RET *buffer;
+
+ int tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid);
+
+ buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
+ if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[i])) != 0)
+ return ret;
+
+ memcpy (myaddr,
+ (char *) buffer + (memaddr & (sizeof (PTRACE_TYPE_RET) - 1)),
+ len);
+
+ return 0;
+}
+
+/* Store LEN bytes from MYADDR to PPU memory at MEMADDR. */
+static int
+store_ppc_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
+{
+ int i, ret;
+
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_TYPE_RET);
+ int count = ((((memaddr + len) - addr) + sizeof (PTRACE_TYPE_RET) - 1)
+ / sizeof (PTRACE_TYPE_RET));
+ PTRACE_TYPE_RET *buffer;
+
+ int tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid);
+
+ buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+
+ if (addr != memaddr || len < (int) sizeof (PTRACE_TYPE_RET))
+ if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[0])) != 0)
+ return ret;
+
+ if (count > 1)
+ if ((ret = fetch_ppc_memory_1 (tid, addr + (count - 1)
+ * sizeof (PTRACE_TYPE_RET),
+ &buffer[count - 1])) != 0)
+ return ret;
+
+ memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_TYPE_RET) - 1)),
+ myaddr, len);
+
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
+ if ((ret = store_ppc_memory_1 (tid, addr, buffer[i])) != 0)
+ return ret;
+
+ return 0;
+}
+
+
+/* If the PPU thread is currently stopped on a spu_run system call,
+ return to FD and ADDR the file handle and NPC parameter address
+ used with the system call. Return non-zero if successful. */
+static int
+parse_spufs_run (int *fd, CORE_ADDR *addr)
+{
+ gdb_byte buf[4];
+ CORE_ADDR pc = fetch_ppc_register (32); /* nip */
+
+ /* Fetch instruction preceding current NIP. */
+ if (fetch_ppc_memory (pc-4, buf, 4) != 0)
+ return 0;
+ /* It should be a "sc" instruction. */
+ if (extract_unsigned_integer (buf, 4) != INSTR_SC)
+ return 0;
+ /* System call number should be NR_spu_run. */
+ if (fetch_ppc_register (0) != NR_spu_run)
+ return 0;
+
+ /* Register 3 contains fd, register 4 the NPC param pointer. */
+ *fd = fetch_ppc_register (34); /* orig_gpr3 */
+ *addr = fetch_ppc_register (4);
+ return 1;
+}
+
+
+/* Copy LEN bytes at OFFSET in spufs file ANNEX into/from READBUF or WRITEBUF,
+ using the /proc file system. */
+static LONGEST
+spu_proc_xfer_spu (const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ char buf[128];
+ int fd = 0;
+ int ret = -1;
+ int pid = PIDGET (inferior_ptid);
+
+ if (!annex)
+ return 0;
+
+ xsnprintf (buf, sizeof buf, "/proc/%d/fd/%s", pid, annex);
+ fd = open (buf, writebuf? O_WRONLY : O_RDONLY);
+ if (fd <= 0)
+ return -1;
+
+ if (offset != 0
+ && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+ {
+ close (fd);
+ return -1;
+ }
+
+ if (writebuf)
+ ret = write (fd, writebuf, (size_t) len);
+ else if (readbuf)
+ ret = read (fd, readbuf, (size_t) len);
+
+ close (fd);
+ return ret;
+}
+
+
+/* Inferior memory should contain an SPE executable image at location ADDR.
+ Allocate a BFD representing that executable. Return NULL on error. */
+
+static void *
+spu_bfd_iovec_open (struct bfd *nbfd, void *open_closure)
+{
+ return open_closure;
+}
+
+static int
+spu_bfd_iovec_close (struct bfd *nbfd, void *stream)
+{
+ xfree (stream);
+ return 1;
+}
+
+static file_ptr
+spu_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
+ file_ptr nbytes, file_ptr offset)
+{
+ CORE_ADDR addr = *(CORE_ADDR *)stream;
+
+ if (fetch_ppc_memory (addr + offset, buf, nbytes) != 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ return nbytes;
+}
+
+static bfd *
+spu_bfd_open (CORE_ADDR addr)
+{
+ struct bfd *nbfd;
+
+ CORE_ADDR *open_closure = xmalloc (sizeof (CORE_ADDR));
+ *open_closure = addr;
+
+ nbfd = bfd_openr_iovec (xstrdup ("<in-memory>"), "elf32-spu",
+ spu_bfd_iovec_open, open_closure,
+ spu_bfd_iovec_pread, spu_bfd_iovec_close);
+ if (!nbfd)
+ return NULL;
+
+ if (!bfd_check_format (nbfd, bfd_object))
+ {
+ bfd_close (nbfd);
+ return NULL;
+ }
+
+ return nbfd;
+}
+
+/* INFERIOR_FD is a file handle passed by the inferior to the
+ spu_run system call. Assuming the SPE context was allocated
+ by the libspe library, try to retrieve the main SPE executable
+ file from its copy within the target process. */
+static void
+spu_symbol_file_add_from_memory (int inferior_fd)
+{
+ CORE_ADDR addr;
+ struct bfd *nbfd;
+
+ char id[128];
+ char annex[32];
+ int len;
+
+ /* Read object ID. */
+ xsnprintf (annex, sizeof annex, "%d/object-id", inferior_fd);
+ len = spu_proc_xfer_spu (annex, id, NULL, 0, sizeof id);
+ if (len <= 0 || len >= sizeof id)
+ return;
+ id[len] = 0;
+ if (sscanf (id, "0x%llx", &addr) != 1)
+ return;
+
+ /* Open BFD representing SPE executable and read its symbols. */
+ nbfd = spu_bfd_open (addr);
+ if (nbfd)
+ symbol_file_add_from_bfd (nbfd, 0, NULL, 1, 0);
+}
+
+
+/* Override the post_startup_inferior routine to continue running
+ the inferior until the first spu_run system call. */
+static void
+spu_child_post_startup_inferior (ptid_t ptid)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ int tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
+ while (!parse_spufs_run (&fd, &addr))
+ {
+ ptrace (PT_SYSCALL, tid, (PTRACE_TYPE_ARG3) 0, 0);
+ waitpid (tid, NULL, __WALL | __WNOTHREAD);
+ }
+}
+
+/* Override the post_attach routine to try load the SPE executable
+ file image from its copy inside the target process. */
+static void
+spu_child_post_attach (int pid)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ /* Like child_post_startup_inferior, if we happened to attach to
+ the inferior while it wasn't currently in spu_run, continue
+ running it until we get back there. */
+ while (!parse_spufs_run (&fd, &addr))
+ {
+ ptrace (PT_SYSCALL, pid, (PTRACE_TYPE_ARG3) 0, 0);
+ waitpid (pid, NULL, __WALL | __WNOTHREAD);
+ }
+
+ /* If the user has not provided an executable file, try to extract
+ the image from inside the target process. */
+ if (!get_exec_file (0))
+ spu_symbol_file_add_from_memory (fd);
+}
+
+/* Wait for child PTID to do something. Return id of the child,
+ minus_one_ptid in case of error; store status into *OURSTATUS. */
+static ptid_t
+spu_child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+{
+ int save_errno;
+ int status;
+ pid_t pid;
+
+ do
+ {
+ set_sigint_trap (); /* Causes SIGINT to be passed on to the
+ attached process. */
+ set_sigio_trap ();
+
+ pid = waitpid (PIDGET (ptid), &status, 0);
+ if (pid == -1 && errno == ECHILD)
+ /* Try again with __WCLONE to check cloned processes. */
+ pid = waitpid (PIDGET (ptid), &status, __WCLONE);
+
+ save_errno = errno;
+
+ /* Make sure we don't report an event for the exit of the
+ original program, if we've detached from it. */
+ if (pid != -1 && !WIFSTOPPED (status) && pid != PIDGET (inferior_ptid))
+ {
+ pid = -1;
+ save_errno = EINTR;
+ }
+
+ clear_sigio_trap ();
+ clear_sigint_trap ();
+ }
+ while (pid == -1 && save_errno == EINTR);
+
+ if (pid == -1)
+ {
+ warning ("Child process unexpectedly missing: %s",
+ safe_strerror (save_errno));
+
+ /* Claim it exited with unknown signal. */
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+ return minus_one_ptid;
+ }
+
+ store_waitstatus (ourstatus, status);
+ return pid_to_ptid (pid);
+}
+
+/* Override the fetch_inferior_register routine. */
+static void
+spu_fetch_inferior_registers (int regno)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ /* We must be stopped on a spu_run system call. */
+ if (!parse_spufs_run (&fd, &addr))
+ return;
+
+ /* The ID register holds the spufs file handle. */
+ if (regno == -1 || regno == SPU_ID_REGNUM)
+ {
+ char buf[4];
+ store_unsigned_integer (buf, 4, fd);
+ regcache_raw_supply (current_regcache, SPU_ID_REGNUM, buf);
+ }
+
+ /* The NPC register is found at ADDR. */
+ if (regno == -1 || regno == SPU_PC_REGNUM)
+ {
+ gdb_byte buf[4];
+ if (fetch_ppc_memory (addr, buf, 4) == 0)
+ regcache_raw_supply (current_regcache, SPU_PC_REGNUM, buf);
+ }
+
+ /* The GPRs are found in the "regs" spufs file. */
+ if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+ {
+ gdb_byte buf[16 * SPU_NUM_GPRS];
+ char annex[32];
+ int i;
+
+ xsnprintf (annex, sizeof annex, "%d/regs", fd);
+ if (spu_proc_xfer_spu (annex, buf, NULL, 0, sizeof buf) == sizeof buf)
+ for (i = 0; i < SPU_NUM_GPRS; i++)
+ regcache_raw_supply (current_regcache, i, buf + i*16);
+ }
+}
+
+/* Override the store_inferior_register routine. */
+static void
+spu_store_inferior_registers (int regno)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ /* We must be stopped on a spu_run system call. */
+ if (!parse_spufs_run (&fd, &addr))
+ return;
+
+ /* The NPC register is found at ADDR. */
+ if (regno == -1 || regno == SPU_PC_REGNUM)
+ {
+ gdb_byte buf[4];
+ regcache_raw_collect (current_regcache, SPU_PC_REGNUM, buf);
+ store_ppc_memory (addr, buf, 4);
+ }
+
+ /* The GPRs are found in the "regs" spufs file. */
+ if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+ {
+ gdb_byte buf[16 * SPU_NUM_GPRS];
+ char annex[32];
+ int i;
+
+ for (i = 0; i < SPU_NUM_GPRS; i++)
+ regcache_raw_collect (current_regcache, i, buf + i*16);
+
+ xsnprintf (annex, sizeof annex, "%d/regs", fd);
+ spu_proc_xfer_spu (annex, NULL, buf, 0, sizeof buf);
+ }
+}
+
+/* Override the to_xfer_partial routine. */
+static LONGEST
+spu_xfer_partial (struct target_ops *ops,
+ enum target_object object, const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ if (object == TARGET_OBJECT_MEMORY)
+ {
+ int fd;
+ CORE_ADDR addr;
+ char mem_annex[32];
+
+ /* We must be stopped on a spu_run system call. */
+ if (!parse_spufs_run (&fd, &addr))
+ return 0;
+
+ /* Use the "mem" spufs file to access SPU local store. */
+ xsnprintf (mem_annex, sizeof mem_annex, "%d/mem", fd);
+ return spu_proc_xfer_spu (mem_annex, readbuf, writebuf, offset, len);
+ }
+
+ return 0;
+}
+
+/* Override the to_can_use_hw_breakpoint routine. */
+static int
+spu_can_use_hw_breakpoint (int type, int cnt, int othertype)
+{
+ return 0;
+}
+
+
+/* Initialize SPU native target. */
+void
+_initialize_spu_nat (void)
+{
+ /* Generic ptrace methods. */
+ struct target_ops *t;
+ t = inf_ptrace_target ();
+
+ /* Add SPU methods. */
+ t->to_post_attach = spu_child_post_attach;
+ t->to_post_startup_inferior = spu_child_post_startup_inferior;
+ t->to_wait = spu_child_wait;
+ t->to_fetch_registers = spu_fetch_inferior_registers;
+ t->to_store_registers = spu_store_inferior_registers;
+ t->to_xfer_partial = spu_xfer_partial;
+ t->to_can_use_hw_breakpoint = spu_can_use_hw_breakpoint;
+
+ /* Register SPU target. */
+ add_target (t);
+}
+
diff -urN gdb-orig/gdb/spu-tdep.c gdb-head/gdb/spu-tdep.c
--- gdb-orig/gdb/spu-tdep.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.c 2006-11-12 15:47:38.159934552 +0100
@@ -0,0 +1,1099 @@
+/* SPU target-dependent code for GDB, the GNU debugger.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+ Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
+ Based on a port by Sid Manning <sid@us.ibm.com>.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+#include "gdb_assert.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "trad-frame.h"
+#include "symtab.h"
+#include "symfile.h"
+#include "value.h"
+#include "inferior.h"
+#include "dis-asm.h"
+#include "objfiles.h"
+#include "language.h"
+#include "regcache.h"
+#include "reggroups.h"
+#include "floatformat.h"
+
+#include "spu-tdep.h"
+
+
+/* Registers. */
+
+static const char *
+spu_register_name (int reg_nr)
+{
+ static char *register_names[] =
+ {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+ "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39",
+ "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47",
+ "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55",
+ "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63",
+ "r64", "r65", "r66", "r67", "r68", "r69", "r70", "r71",
+ "r72", "r73", "r74", "r75", "r76", "r77", "r78", "r79",
+ "r80", "r81", "r82", "r83", "r84", "r85", "r86", "r87",
+ "r88", "r89", "r90", "r91", "r92", "r93", "r94", "r95",
+ "r96", "r97", "r98", "r99", "r100", "r101", "r102", "r103",
+ "r104", "r105", "r106", "r107", "r108", "r109", "r110", "r111",
+ "r112", "r113", "r114", "r115", "r116", "r117", "r118", "r119",
+ "r120", "r121", "r122", "r123", "r124", "r125", "r126", "r127",
+ "id", "pc", "sp"
+ };
+
+ if (reg_nr < 0)
+ return NULL;
+ if (reg_nr >= sizeof register_names / sizeof *register_names)
+ return NULL;
+
+ return register_names[reg_nr];
+}
+
+static struct type *
+spu_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+ if (reg_nr < SPU_NUM_GPRS)
+ return builtin_type_vec128;
+
+ switch (reg_nr)
+ {
+ case SPU_ID_REGNUM:
+ return builtin_type_uint32;
+
+ case SPU_PC_REGNUM:
+ return builtin_type_void_func_ptr;
+
+ case SPU_SP_REGNUM:
+ return builtin_type_void_data_ptr;
+
+ default:
+ internal_error (__FILE__, __LINE__, "invalid regnum");
+ }
+}
+
+/* Pseudo registers for preferred slots - stack pointer. */
+
+static void
+spu_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
+ int regnum, gdb_byte *buf)
+{
+ gdb_byte reg[16];
+
+ switch (regnum)
+ {
+ case SPU_SP_REGNUM:
+ regcache_raw_read (regcache, SPU_RAW_SP_REGNUM, reg);
+ memcpy (buf, reg, 4);
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__, _("invalid regnum"));
+ }
+}
+
+static void
+spu_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
+ int regnum, const gdb_byte *buf)
+{
+ gdb_byte reg[16];
+
+ switch (regnum)
+ {
+ case SPU_SP_REGNUM:
+ regcache_raw_read (regcache, SPU_RAW_SP_REGNUM, reg);
+ memcpy (reg, buf, 4);
+ regcache_raw_write (regcache, SPU_RAW_SP_REGNUM, reg);
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__, _("invalid regnum"));
+ }
+}
+
+/* Value conversion -- access scalar values at the preferred slot. */
+
+static int
+spu_convert_register_p (int regno, struct type *type)
+{
+ return regno < SPU_NUM_GPRS && TYPE_LENGTH (type) < 16;
+}
+
+static void
+spu_register_to_value (struct frame_info *frame, int regnum,
+ struct type *valtype, gdb_byte *out)
+{
+ gdb_byte in[16];
+ int len = TYPE_LENGTH (valtype);
+ int preferred_slot = len < 4 ? 4 - len : 0;
+ gdb_assert (len < 16);
+
+ get_frame_register (frame, regnum, in);
+ memcpy (out, in + preferred_slot, len);
+}
+
+static void
+spu_value_to_register (struct frame_info *frame, int regnum,
+ struct type *valtype, const gdb_byte *in)
+{
+ gdb_byte out[16];
+ int len = TYPE_LENGTH (valtype);
+ int preferred_slot = len < 4 ? 4 - len : 0;
+ gdb_assert (len < 16);
+
+ memset (out, 0, 16);
+ memcpy (out + preferred_slot, in, len);
+ put_frame_register (frame, regnum, out);
+}
+
+/* Register groups. */
+
+static int
+spu_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+ struct reggroup *group)
+{
+ /* Registers displayed via 'info regs'. */
+ if (group == general_reggroup)
+ return 1;
+
+ /* Registers displayed via 'info float'. */
+ if (group == float_reggroup)
+ return 0;
+
+ /* Registers that need to be saved/restored in order to
+ push or pop frames. */
+ if (group == save_reggroup || group == restore_reggroup)
+ return 1;
+
+ return default_register_reggroup_p (gdbarch, regnum, group);
+}
+
+
+/* Decoding SPU instructions. */
+
+enum
+ {
+ op_lqd = 0x34,
+ op_lqx = 0x3c4,
+ op_lqa = 0x61,
+ op_lqr = 0x67,
+ op_stqd = 0x24,
+ op_stqx = 0x144,
+ op_stqa = 0x41,
+ op_stqr = 0x47,
+
+ op_il = 0x081,
+ op_ila = 0x21,
+ op_a = 0x0c0,
+ op_ai = 0x1c,
+
+ op_selb = 0x4,
+
+ op_br = 0x64,
+ op_bra = 0x60,
+ op_brsl = 0x66,
+ op_brasl = 0x62,
+ op_brnz = 0x42,
+ op_brz = 0x40,
+ op_brhnz = 0x46,
+ op_brhz = 0x44,
+ op_bi = 0x1a8,
+ op_bisl = 0x1a9,
+ op_biz = 0x128,
+ op_binz = 0x129,
+ op_bihz = 0x12a,
+ op_bihnz = 0x12b,
+ };
+
+static int
+is_rr (unsigned int insn, int op, int *rt, int *ra, int *rb)
+{
+ if ((insn >> 21) == op)
+ {
+ *rt = insn & 127;
+ *ra = (insn >> 7) & 127;
+ *rb = (insn >> 14) & 127;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+is_rrr (unsigned int insn, int op, int *rt, int *ra, int *rb, int *rc)
+{
+ if ((insn >> 28) == op)
+ {
+ *rt = (insn >> 21) & 127;
+ *ra = (insn >> 7) & 127;
+ *rb = (insn >> 14) & 127;
+ *rc = insn & 127;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+is_ri7 (unsigned int insn, int op, int *rt, int *ra, int *i7)
+{
+ if ((insn >> 21) == op)
+ {
+ *rt = insn & 127;
+ *ra = (insn >> 7) & 127;
+ *i7 = (((insn >> 14) & 127) ^ 0x40) - 0x40;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+is_ri10 (unsigned int insn, int op, int *rt, int *ra, int *i10)
+{
+ if ((insn >> 24) == op)
+ {
+ *rt = insn & 127;
+ *ra = (insn >> 7) & 127;
+ *i10 = (((insn >> 14) & 0x3ff) ^ 0x200) - 0x200;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+is_ri16 (unsigned int insn, int op, int *rt, int *i16)
+{
+ if ((insn >> 23) == op)
+ {
+ *rt = insn & 127;
+ *i16 = (((insn >> 7) & 0xffff) ^ 0x8000) - 0x8000;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+is_ri18 (unsigned int insn, int op, int *rt, int *i18)
+{
+ if ((insn >> 25) == op)
+ {
+ *rt = insn & 127;
+ *i18 = (((insn >> 7) & 0x3ffff) ^ 0x20000) - 0x20000;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+is_branch (unsigned int insn, int *offset, int *reg)
+{
+ int rt, i7, i16;
+
+ if (is_ri16 (insn, op_br, &rt, &i16)
+ || is_ri16 (insn, op_brsl, &rt, &i16)
+ || is_ri16 (insn, op_brnz, &rt, &i16)
+ || is_ri16 (insn, op_brz, &rt, &i16)
+ || is_ri16 (insn, op_brhnz, &rt, &i16)
+ || is_ri16 (insn, op_brhz, &rt, &i16))
+ {
+ *reg = SPU_PC_REGNUM;
+ *offset = i16 << 2;
+ return 1;
+ }
+
+ if (is_ri16 (insn, op_bra, &rt, &i16)
+ || is_ri16 (insn, op_brasl, &rt, &i16))
+ {
+ *reg = -1;
+ *offset = i16 << 2;
+ return 1;
+ }
+
+ if (is_ri7 (insn, op_bi, &rt, reg, &i7)
+ || is_ri7 (insn, op_bisl, &rt, reg, &i7)
+ || is_ri7 (insn, op_biz, &rt, reg, &i7)
+ || is_ri7 (insn, op_binz, &rt, reg, &i7)
+ || is_ri7 (insn, op_bihz, &rt, reg, &i7)
+ || is_ri7 (insn, op_bihnz, &rt, reg, &i7))
+ {
+ *offset = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Prolog parsing. */
+
+struct spu_prologue_data
+ {
+ /* Stack frame size. -1 if analysis was unsuccessful. */
+ int size;
+
+ /* How to find the CFA. The CFA is equal to SP at function entry. */
+ int cfa_reg;
+ int cfa_offset;
+
+ /* Offset relative to CFA where a register is saved. -1 if invalid. */
+ int reg_offset[SPU_NUM_GPRS];
+ };
+
+static CORE_ADDR
+spu_analyze_prologue (CORE_ADDR start_pc, CORE_ADDR end_pc,
+ struct spu_prologue_data *data)
+{
+ int found_sp = 0;
+ int found_fp = 0;
+ int found_lr = 0;
+ int reg_immed[SPU_NUM_GPRS];
+ gdb_byte buf[16];
+ CORE_ADDR prolog_pc = start_pc;
+ CORE_ADDR pc;
+ int i;
+
+
+ /* Initialize DATA to default values. */
+ data->size = -1;
+
+ data->cfa_reg = SPU_RAW_SP_REGNUM;
+ data->cfa_offset = 0;
+
+ for (i = 0; i < SPU_NUM_GPRS; i++)
+ data->reg_offset[i] = -1;
+
+ /* Set up REG_IMMED array. This is non-zero for a register if we know its
+ preferred slot currently holds this immediate value. */
+ for (i = 0; i < SPU_NUM_GPRS; i++)
+ reg_immed[i] = 0;
+
+ /* Scan instructions until the first branch.
+
+ The following instructions are important prolog components:
+
+ - The first instruction to set up the stack pointer.
+ - The first instruction to set up the frame pointer.
+ - The first instruction to save the link register.
+
+ We return the instruction after the latest of these three,
+ or the incoming PC if none is found. The first instruction
+ to set up the stack pointer also defines the frame size.
+
+ Note that instructions saving incoming arguments to their stack
+ slots are not counted as important, because they are hard to
+ identify with certainty. This should not matter much, because
+ arguments are relevant only in code compiled with debug data,
+ and in such code the GDB core will advance until the first source
+ line anyway, using SAL data.
+
+ For purposes of stack unwinding, we analyze the following types
+ of instructions in addition:
+
+ - Any instruction adding to the current frame pointer.
+ - Any instruction loading an immediate constant into a register.
+ - Any instruction storing a register onto the stack.
+
+ These are used to compute the CFA and REG_OFFSET output. */
+
+ for (pc = start_pc; pc < end_pc; pc += 4)
+ {
+ unsigned int insn;
+ int rt, ra, rb, rc, immed;
+
+ if (target_read_memory (pc, buf, 4))
+ break;
+ insn = extract_unsigned_integer (buf, 4);
+
+ /* AI is the typical instruction to set up a stack frame.
+ It is also used to initialize the frame pointer. */
+ if (is_ri10 (insn, op_ai, &rt, &ra, &immed))
+ {
+ if (rt == data->cfa_reg && ra == data->cfa_reg)
+ data->cfa_offset -= immed;
+
+ if (rt == SPU_RAW_SP_REGNUM && ra == SPU_RAW_SP_REGNUM
+ && !found_sp)
+ {
+ found_sp = 1;
+ prolog_pc = pc + 4;
+
+ data->size = -immed;
+ }
+ else if (rt == SPU_FP_REGNUM && ra == SPU_RAW_SP_REGNUM
+ && !found_fp)
+ {
+ found_fp = 1;
+ prolog_pc = pc + 4;
+
+ data->cfa_reg = SPU_FP_REGNUM;
+ data->cfa_offset -= immed;
+ }
+ }
+
+ /* A is used to set up stack frames of size >= 512 bytes.
+ If we have tracked the contents of the addend register,
+ we can handle this as well. */
+ else if (is_rr (insn, op_a, &rt, &ra, &rb))
+ {
+ if (rt == data->cfa_reg && ra == data->cfa_reg)
+ {
+ if (reg_immed[rb] != 0)
+ data->cfa_offset -= reg_immed[rb];
+ else
+ data->cfa_reg = -1; /* We don't know the CFA any more. */
+ }
+
+ if (rt == SPU_RAW_SP_REGNUM && ra == SPU_RAW_SP_REGNUM
+ && !found_sp)
+ {
+ found_sp = 1;
+ prolog_pc = pc + 4;
+
+ if (reg_immed[rb] != 0)
+ data->size = -reg_immed[rb];
+ }
+ }
+
+ /* We need to track IL and ILA used to load immediate constants
+ in case they are later used as input to an A instruction. */
+ else if (is_ri16 (insn, op_il, &rt, &immed))
+ {
+ reg_immed[rt] = immed;
+ }
+
+ else if (is_ri18 (insn, op_ila, &rt, &immed))
+ {
+ reg_immed[rt] = immed & 0x3ffff;
+ }
+
+ /* STQD is used to save registers to the stack. */
+ else if (is_ri10 (insn, op_stqd, &rt, &ra, &immed))
+ {
+ if (ra == data->cfa_reg)
+ data->reg_offset[rt] = data->cfa_offset - (immed << 4);
+
+ if (ra == data->cfa_reg && rt == SPU_LR_REGNUM
+ && !found_lr)
+ {
+ found_lr = 1;
+ prolog_pc = pc + 4;
+ }
+ }
+
+ /* _start uses SELB to set up the stack pointer. */
+ else if (is_rrr (insn, op_selb, &rt, &ra, &rb, &rc))
+ {
+ if (rt == SPU_RAW_SP_REGNUM && !found_sp)
+ found_sp = 1;
+ }
+
+ /* We terminate if we find a branch. */
+ else if (is_branch (insn, &immed, &ra))
+ break;
+ }
+
+
+ /* If we successfully parsed until here, and didn't find any instruction
+ modifying SP, we assume we have a frameless function. */
+ if (!found_sp)
+ data->size = 0;
+
+ /* Return cooked instead of raw SP. */
+ if (data->cfa_reg == SPU_RAW_SP_REGNUM)
+ data->cfa_reg = SPU_SP_REGNUM;
+
+ return prolog_pc;
+}
+
+/* Return the first instruction after the prologue starting at PC. */
+static CORE_ADDR
+spu_skip_prologue (CORE_ADDR pc)
+{
+ struct spu_prologue_data data;
+ return spu_analyze_prologue (pc, (CORE_ADDR)-1, &data);
+}
+
+/* Return the frame pointer in use at address PC. */
+static void
+spu_virtual_frame_pointer (CORE_ADDR pc, int *reg, LONGEST *offset)
+{
+ struct spu_prologue_data data;
+ spu_analyze_prologue (pc, (CORE_ADDR)-1, &data);
+
+ if (data.size != -1 && data.cfa_reg != -1)
+ {
+ /* The 'frame pointer' address is CFA minus frame size. */
+ *reg = data.cfa_reg;
+ *offset = data.cfa_offset - data.size;
+ }
+ else
+ {
+ /* ??? We don't really know ... */
+ *reg = SPU_SP_REGNUM;
+ *offset = 0;
+ }
+}
+
+/* Normal stack frames. */
+
+struct spu_unwind_cache
+{
+ CORE_ADDR func;
+ CORE_ADDR frame_base;
+ CORE_ADDR local_base;
+
+ struct trad_frame_saved_reg *saved_regs;
+};
+
+static struct spu_unwind_cache *
+spu_frame_unwind_cache (struct frame_info *next_frame,
+ void **this_prologue_cache)
+{
+ struct spu_unwind_cache *info;
+ struct spu_prologue_data data;
+
+ if (*this_prologue_cache)
+ return *this_prologue_cache;
+
+ info = FRAME_OBSTACK_ZALLOC (struct spu_unwind_cache);
+ *this_prologue_cache = info;
+ info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+ info->frame_base = 0;
+ info->local_base = 0;
+
+ /* Find the start of the current function, and analyze its prologue. */
+ info->func = frame_func_unwind (next_frame);
+ if (info->func == 0)
+ {
+ /* Fall back to using the current PC as frame ID. */
+ info->func = frame_pc_unwind (next_frame);
+ data.size = -1;
+ }
+ else
+ spu_analyze_prologue (info->func, frame_pc_unwind (next_frame), &data);
+
+
+ /* If successful, use prologue analysis data. */
+ if (data.size != -1 && data.cfa_reg != -1)
+ {
+ CORE_ADDR cfa;
+ int i;
+ gdb_byte buf[16];
+
+ /* Determine CFA via unwound CFA_REG plus CFA_OFFSET. */
+ frame_unwind_register (next_frame, data.cfa_reg, buf);
+ cfa = extract_unsigned_integer (buf, 4) + data.cfa_offset;
+
+ /* Call-saved register slots. */
+ for (i = 0; i < SPU_NUM_GPRS; i++)
+ if (i == SPU_LR_REGNUM
+ || (i >= SPU_SAVED1_REGNUM && i <= SPU_SAVEDN_REGNUM))
+ if (data.reg_offset[i] != -1)
+ info->saved_regs[i].addr = cfa - data.reg_offset[i];
+
+ /* The previous PC comes from the link register. */
+ if (trad_frame_addr_p (info->saved_regs, SPU_LR_REGNUM))
+ info->saved_regs[SPU_PC_REGNUM] = info->saved_regs[SPU_LR_REGNUM];
+ else
+ info->saved_regs[SPU_PC_REGNUM].realreg = SPU_LR_REGNUM;
+
+ /* The previous SP is equal to the CFA. */
+ trad_frame_set_value (info->saved_regs, SPU_SP_REGNUM, cfa);
+
+ /* Frame bases. */
+ info->frame_base = cfa;
+ info->local_base = cfa - data.size;
+ }
+
+ /* Otherwise, fall back to reading the backchain link. */
+ else
+ {
+ CORE_ADDR reg, backchain;
+
+ /* Get the backchain. */
+ reg = frame_unwind_register_unsigned (next_frame, SPU_SP_REGNUM);
+ backchain = read_memory_unsigned_integer (reg, 4);
+
+ /* A zero backchain terminates the frame chain. Also, sanity
+ check against the local store size limit. */
+ if (backchain != 0 && backchain < SPU_LS_SIZE)
+ {
+ /* Assume the link register is saved into its slot. */
+ if (backchain + 16 < SPU_LS_SIZE)
+ info->saved_regs[SPU_LR_REGNUM].addr = backchain + 16;
+
+ /* This will also be the previous PC. */
+ if (trad_frame_addr_p (info->saved_regs, SPU_LR_REGNUM))
+ info->saved_regs[SPU_PC_REGNUM] = info->saved_regs[SPU_LR_REGNUM];
+ else
+ info->saved_regs[SPU_PC_REGNUM].realreg = SPU_LR_REGNUM;
+
+ /* The previous SP will equal the backchain value. */
+ trad_frame_set_value (info->saved_regs, SPU_SP_REGNUM, backchain);
+
+ /* Frame bases. */
+ info->frame_base = backchain;
+ info->local_base = reg;
+ }
+ }
+
+ return info;
+}
+
+static void
+spu_frame_this_id (struct frame_info *next_frame,
+ void **this_prologue_cache, struct frame_id *this_id)
+{
+ struct spu_unwind_cache *info =
+ spu_frame_unwind_cache (next_frame, this_prologue_cache);
+
+ if (info->frame_base == 0)
+ return;
+
+ *this_id = frame_id_build (info->frame_base, info->func);
+}
+
+static void
+spu_frame_prev_register (struct frame_info *next_frame,
+ void **this_prologue_cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR * addrp,
+ int *realnump, gdb_byte *bufferp)
+{
+ struct spu_unwind_cache *info
+ = spu_frame_unwind_cache (next_frame, this_prologue_cache);
+
+ /* Special-case the stack pointer. */
+ if (regnum == SPU_RAW_SP_REGNUM)
+ regnum = SPU_SP_REGNUM;
+
+ trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
+ optimizedp, lvalp, addrp, realnump, bufferp);
+}
+
+static const struct frame_unwind spu_frame_unwind = {
+ NORMAL_FRAME,
+ spu_frame_this_id,
+ spu_frame_prev_register
+};
+
+const struct frame_unwind *
+spu_frame_sniffer (struct frame_info *next_frame)
+{
+ return &spu_frame_unwind;
+}
+
+static CORE_ADDR
+spu_frame_base_address (struct frame_info *next_frame, void **this_cache)
+{
+ struct spu_unwind_cache *info
+ = spu_frame_unwind_cache (next_frame, this_cache);
+ return info->local_base;
+}
+
+static const struct frame_base spu_frame_base = {
+ &spu_frame_unwind,
+ spu_frame_base_address,
+ spu_frame_base_address,
+ spu_frame_base_address
+};
+
+static CORE_ADDR
+spu_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_unwind_register_unsigned (next_frame, SPU_PC_REGNUM);
+}
+
+static CORE_ADDR
+spu_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_unwind_register_unsigned (next_frame, SPU_SP_REGNUM);
+}
+
+
+/* Function calling convention. */
+
+static int
+spu_scalar_value_p (struct type *type)
+{
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_INT:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_BOOL:
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_REF:
+ return TYPE_LENGTH (type) <= 16;
+
+ default:
+ return 0;
+ }
+}
+
+static void
+spu_value_to_regcache (struct regcache *regcache, int regnum,
+ struct type *type, const gdb_byte *in)
+{
+ int len = TYPE_LENGTH (type);
+
+ if (spu_scalar_value_p (type))
+ {
+ int preferred_slot = len < 4 ? 4 - len : 0;
+ regcache_cooked_write_part (regcache, regnum, preferred_slot, len, in);
+ }
+ else
+ {
+ while (len >= 16)
+ {
+ regcache_cooked_write (regcache, regnum++, in);
+ in += 16;
+ len -= 16;
+ }
+
+ if (len > 0)
+ regcache_cooked_write_part (regcache, regnum, 0, len, in);
+ }
+}
+
+static void
+spu_regcache_to_value (struct regcache *regcache, int regnum,
+ struct type *type, gdb_byte *out)
+{
+ int len = TYPE_LENGTH (type);
+
+ if (spu_scalar_value_p (type))
+ {
+ int preferred_slot = len < 4 ? 4 - len : 0;
+ regcache_cooked_read_part (regcache, regnum, preferred_slot, len, out);
+ }
+ else
+ {
+ while (len >= 16)
+ {
+ regcache_cooked_read (regcache, regnum++, out);
+ out += 16;
+ len -= 16;
+ }
+
+ if (len > 0)
+ regcache_cooked_read_part (regcache, regnum, 0, len, out);
+ }
+}
+
+static CORE_ADDR
+spu_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
+ struct regcache *regcache, CORE_ADDR bp_addr,
+ int nargs, struct value **args, CORE_ADDR sp,
+ int struct_return, CORE_ADDR struct_addr)
+{
+ int i;
+ int regnum = SPU_ARG1_REGNUM;
+ int stack_arg = -1;
+ gdb_byte buf[16];
+
+ /* Set the return address. */
+ memset (buf, 0, sizeof buf);
+ store_unsigned_integer (buf, 4, bp_addr);
+ regcache_cooked_write (regcache, SPU_LR_REGNUM, buf);
+
+ /* If STRUCT_RETURN is true, then the struct return address (in
+ STRUCT_ADDR) will consume the first argument-passing register.
+ Both adjust the register count and store that value. */
+ if (struct_return)
+ {
+ memset (buf, 0, sizeof buf);
+ store_unsigned_integer (buf, 4, struct_addr);
+ regcache_cooked_write (regcache, regnum++, buf);
+ }
+
+ /* Fill in argument registers. */
+ for (i = 0; i < nargs; i++)
+ {
+ struct value *arg = args[i];
+ struct type *type = check_typedef (value_type (arg));
+ const gdb_byte *contents = value_contents (arg);
+ int len = TYPE_LENGTH (type);
+ int n_regs = align_up (len, 16) / 16;
+
+ /* If the argument doesn't wholly fit into registers, it and
+ all subsequent arguments go to the stack. */
+ if (regnum + n_regs - 1 > SPU_ARGN_REGNUM)
+ {
+ stack_arg = i;
+ break;
+ }
+
+ spu_value_to_regcache (regcache, regnum, type, contents);
+ regnum += n_regs;
+ }
+
+ /* Overflow arguments go to the stack. */
+ if (stack_arg != -1)
+ {
+ CORE_ADDR ap;
+
+ /* Allocate all required stack size. */
+ for (i = stack_arg; i < nargs; i++)
+ {
+ struct type *type = check_typedef (value_type (args[i]));
+ sp -= align_up (TYPE_LENGTH (type), 16);
+ }
+
+ /* Fill in stack arguments. */
+ ap = sp;
+ for (i = stack_arg; i < nargs; i++)
+ {
+ struct value *arg = args[i];
+ struct type *type = check_typedef (value_type (arg));
+ int len = TYPE_LENGTH (type);
+ int preferred_slot;
+
+ if (spu_scalar_value_p (type))
+ preferred_slot = len < 4 ? 4 - len : 0;
+ else
+ preferred_slot = 0;
+
+ target_write_memory (ap + preferred_slot, value_contents (arg), len);
+ ap += align_up (TYPE_LENGTH (type), 16);
+ }
+ }
+
+ /* Allocate stack frame header. */
+ sp -= 32;
+
+ /* Finally, update the SP register. */
+ regcache_cooked_write_unsigned (regcache, SPU_SP_REGNUM, sp);
+
+ return sp;
+}
+
+static struct frame_id
+spu_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_id_build (spu_unwind_sp (gdbarch, next_frame),
+ spu_unwind_pc (gdbarch, next_frame));
+}
+
+/* Function return value access. */
+
+static enum return_value_convention
+spu_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache, gdb_byte *out, const gdb_byte *in)
+{
+ enum return_value_convention rvc;
+
+ if (TYPE_LENGTH (type) <= (SPU_ARGN_REGNUM - SPU_ARG1_REGNUM + 1) * 16)
+ rvc = RETURN_VALUE_REGISTER_CONVENTION;
+ else
+ rvc = RETURN_VALUE_STRUCT_CONVENTION;
+
+ if (in)
+ {
+ switch (rvc)
+ {
+ case RETURN_VALUE_REGISTER_CONVENTION:
+ spu_value_to_regcache (regcache, SPU_ARG1_REGNUM, type, in);
+ break;
+
+ case RETURN_VALUE_STRUCT_CONVENTION:
+ error ("Cannot set function return value.");
+ break;
+ }
+ }
+ else if (out)
+ {
+ switch (rvc)
+ {
+ case RETURN_VALUE_REGISTER_CONVENTION:
+ spu_regcache_to_value (regcache, SPU_ARG1_REGNUM, type, out);
+ break;
+
+ case RETURN_VALUE_STRUCT_CONVENTION:
+ error ("Function return value unknown.");
+ break;
+ }
+ }
+
+ return rvc;
+}
+
+
+/* Breakpoints. */
+
+static const gdb_byte *
+spu_breakpoint_from_pc (CORE_ADDR * pcptr, int *lenptr)
+{
+ static const gdb_byte breakpoint[] = { 0x00, 0x00, 0x3f, 0xff };
+
+ *lenptr = sizeof breakpoint;
+ return breakpoint;
+}
+
+
+/* Software single-stepping support. */
+
+void
+spu_software_single_step (enum target_signal signal, int insert_breakpoints_p)
+{
+ if (insert_breakpoints_p)
+ {
+ CORE_ADDR pc, next_pc;
+ unsigned int insn;
+ int offset, reg;
+ gdb_byte buf[4];
+
+ regcache_cooked_read (current_regcache, SPU_PC_REGNUM, buf);
+ pc = extract_unsigned_integer (buf, 4);
+
+ if (target_read_memory (pc, buf, 4))
+ return;
+ insn = extract_unsigned_integer (buf, 4);
+
+ /* Next sequential instruction is at PC + 4, except if the current
+ instruction is a PPE-assisted call, in which case it is at PC + 8.
+ Wrap around LS limit to be on the safe side. */
+ if ((insn & 0xffffff00) == 0x00002100)
+ next_pc = (pc + 8) & (SPU_LS_SIZE - 1) & -4;
+ else
+ next_pc = (pc + 4) & (SPU_LS_SIZE - 1) & -4;
+
+ insert_single_step_breakpoint (next_pc);
+
+ if (is_branch (insn, &offset, &reg))
+ {
+ CORE_ADDR target = offset;
+
+ if (reg == SPU_PC_REGNUM)
+ target += pc;
+ else if (reg != -1)
+ {
+ regcache_cooked_read_part (current_regcache, reg, 0, 4, buf);
+ target += extract_unsigned_integer (buf, 4);
+ }
+
+ target = target & (SPU_LS_SIZE - 1) & -4;
+ if (target != next_pc)
+ insert_single_step_breakpoint (target);
+ }
+ }
+ else
+ remove_single_step_breakpoints ();
+}
+
+
+/* Set up gdbarch struct. */
+
+static struct gdbarch *
+spu_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+ struct gdbarch *gdbarch;
+
+ /* Find a candidate among the list of pre-declared architectures. */
+ arches = gdbarch_list_lookup_by_info (arches, &info);
+ if (arches != NULL)
+ return arches->gdbarch;
+
+ /* Is is for us? */
+ if (info.bfd_arch_info->mach != bfd_mach_spu)
+ return NULL;
+
+ /* Yes, create a new architecture. */
+ gdbarch = gdbarch_alloc (&info, NULL);
+
+ /* Disassembler. */
+ set_gdbarch_print_insn (gdbarch, print_insn_spu);
+
+ /* Registers. */
+ set_gdbarch_num_regs (gdbarch, SPU_NUM_REGS);
+ set_gdbarch_num_pseudo_regs (gdbarch, SPU_NUM_PSEUDO_REGS);
+ set_gdbarch_sp_regnum (gdbarch, SPU_SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, SPU_PC_REGNUM);
+ set_gdbarch_register_name (gdbarch, spu_register_name);
+ set_gdbarch_register_type (gdbarch, spu_register_type);
+ set_gdbarch_pseudo_register_read (gdbarch, spu_pseudo_register_read);
+ set_gdbarch_pseudo_register_write (gdbarch, spu_pseudo_register_write);
+ set_gdbarch_convert_register_p (gdbarch, spu_convert_register_p);
+ set_gdbarch_register_to_value (gdbarch, spu_register_to_value);
+ set_gdbarch_value_to_register (gdbarch, spu_value_to_register);
+ set_gdbarch_register_reggroup_p (gdbarch, spu_register_reggroup_p);
+
+ /* Data types. */
+ set_gdbarch_char_signed (gdbarch, 0);
+ set_gdbarch_ptr_bit (gdbarch, 32);
+ set_gdbarch_addr_bit (gdbarch, 32);
+ set_gdbarch_short_bit (gdbarch, 16);
+ set_gdbarch_int_bit (gdbarch, 32);
+ set_gdbarch_long_bit (gdbarch, 32);
+ set_gdbarch_long_long_bit (gdbarch, 64);
+ set_gdbarch_float_bit (gdbarch, 32);
+ set_gdbarch_double_bit (gdbarch, 64);
+ set_gdbarch_long_double_bit (gdbarch, 64);
+ set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_big);
+ set_gdbarch_double_format (gdbarch, &floatformat_ieee_double_big);
+ set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_double_big);
+
+ /* Inferior function calls. */
+ set_gdbarch_push_dummy_call (gdbarch, spu_push_dummy_call);
+ set_gdbarch_unwind_dummy_id (gdbarch, spu_unwind_dummy_id);
+ set_gdbarch_return_value (gdbarch, spu_return_value);
+
+ /* Frame handling. */
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ frame_unwind_append_sniffer (gdbarch, spu_frame_sniffer);
+ frame_base_set_default (gdbarch, &spu_frame_base);
+ set_gdbarch_unwind_pc (gdbarch, spu_unwind_pc);
+ set_gdbarch_unwind_sp (gdbarch, spu_unwind_sp);
+ set_gdbarch_virtual_frame_pointer (gdbarch, spu_virtual_frame_pointer);
+ set_gdbarch_frame_args_skip (gdbarch, 0);
+ set_gdbarch_skip_prologue (gdbarch, spu_skip_prologue);
+
+ /* Breakpoints. */
+ set_gdbarch_decr_pc_after_break (gdbarch, 4);
+ set_gdbarch_breakpoint_from_pc (gdbarch, spu_breakpoint_from_pc);
+ set_gdbarch_cannot_step_breakpoint (gdbarch, 1);
+ set_gdbarch_software_single_step (gdbarch, spu_software_single_step);
+
+ return gdbarch;
+}
+
+void
+_initialize_spu_tdep (void)
+{
+ register_gdbarch_init (bfd_arch_spu, spu_gdbarch_init);
+}
diff -urN gdb-orig/gdb/spu-tdep.h gdb-head/gdb/spu-tdep.h
--- gdb-orig/gdb/spu-tdep.h 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.h 2006-11-12 15:58:09.260991552 +0100
@@ -0,0 +1,50 @@
+/* SPU target-dependent code for GDB, the GNU debugger.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef SPU_TDEP_H
+#define SPU_TDEP_H
+
+/* Number of registers. */
+#define SPU_NUM_REGS 130
+#define SPU_NUM_PSEUDO_REGS 1
+#define SPU_NUM_GPRS 128
+
+/* Register numbers of various important registers. */
+enum spu_regnum
+{
+ /* SPU calling convention. */
+ SPU_LR_REGNUM = 0, /* Link register. */
+ SPU_RAW_SP_REGNUM = 1, /* Stack pointer (full register). */
+ SPU_ARG1_REGNUM = 3, /* First argument register. */
+ SPU_ARGN_REGNUM = 74, /* Last argument register. */
+ SPU_SAVED1_REGNUM = 80, /* First call-saved register. */
+ SPU_SAVEDN_REGNUM = 127, /* Last call-saved register. */
+ SPU_FP_REGNUM = 127, /* Frame pointer. */
+
+ /* Special registers. */
+ SPU_ID_REGNUM = 128, /* SPU ID register. */
+ SPU_PC_REGNUM = 129, /* Next program counter. */
+ SPU_SP_REGNUM = 130 /* Stack pointer (preferred slot). */
+};
+
+/* Local store. */
+#define SPU_LS_SIZE 0x40000
+
+#endif

--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com