This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
[rfa?] Implement ppc32 SYSV {extract,store} return value
- From: Andrew Cagney <ac131313 at redhat dot com>
- To: gdb-patches at sources dot redhat dot com,Kevin Buettner <kevinb at redhat dot com>,Jason R Thorpe <thorpej at wasabisystems dot com>
- Date: Wed, 17 Sep 2003 17:54:49 -0400
- Subject: [rfa?] Implement ppc32 SYSV {extract,store} return value
Hello,
This adds a new ppc32 SYSV specific extract_return_value and
store_return_value (while at the same time eliminating the e500 specific
methods). The new functions also avoid some really bogus looking
floating point code in the old rs6000 * return_value functions - I'm
still trying to figure out how it worked ... ah, it's native only.
I've tested it in vanila GNU/Linux (no regressions) and NBSD (4 less
structs fails) (i.e., no altivec nor e500).
If nothing else I'd really like a comment on the general approach taken
- the netbsd function wrapping a more generic method, and a better word
than "broken" I guess.
thoughts?
Andrew
PS: My comments refer to SYSV ppc32 but the change does also affect SYSV
ppc64, "can I get back to you on that" :-)
2003-09-13 Andrew Cagney <cagney@redhat.com>
* rs6000-tdep.c (e500_store_return_value): Delete function.
(e500_extract_return_value): Delete function.
(rs6000_gdbarch_init): When SYSV, set "extract_return_value" and
"restore_return_value" to "ppc_sysv_abi_extract_return_value" and
"ppc_sysv_abi_restore_return_value" where applicable.
* ppc-tdep.h: Re-organize so that functions from "ppc-sysv-tdep.c"
are all the one place.
(ppc_sysv_abi_store_return_value): Declare.
(ppc_sysv_abi_extract_return_value): Declare.
(ppc_sysv_abi_broken_store_return_value): Declare.
(ppc_sysv_abi_broken_extract_return_value): Declare.
* ppc-sysv-tdep.c (extract_return_value): New function.
(store_return_value): New function.
(ppc_sysv_abi_extract_return_value): New function.
(ppc_sysv_abi_store_return_value): New function.
(ppc_sysv_abi_broken_extract_return_value): New function.
(ppc_sysv_abi_broken_store_return_value): New function.
Index: ppc-sysv-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-sysv-tdep.c,v
retrieving revision 1.11
diff -u -r1.11 ppc-sysv-tdep.c
--- ppc-sysv-tdep.c 16 Sep 2003 23:33:17 -0000 1.11
+++ ppc-sysv-tdep.c 17 Sep 2003 21:22:27 -0000
@@ -340,3 +340,228 @@
return (TYPE_LENGTH (value_type) > 8);
}
+
+static void
+extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf, int broken)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ if (using_struct_return (type, 1))
+ error ("Function return value unknown");
+ else if (TYPE_CODE (type) == TYPE_CODE_FLT
+ && TYPE_LENGTH (type) <= 8
+ && ppc_floating_point_unit_p (current_gdbarch))
+ {
+ /* Floats and doubles stored in "f1". Convert the value to the
+ required type. */
+ char regval[MAX_REGISTER_SIZE];
+ struct type *regtype = register_type (current_gdbarch, FP0_REGNUM + 1);
+ regcache_cooked_read (regcache, FP0_REGNUM + 1, regval);
+ convert_typed_floating (regval, regtype, valbuf, type);
+ }
+ else if ((TYPE_CODE (type) == TYPE_CODE_INT
+ && TYPE_LENGTH (type) == 8
+ && tdep->wordsize == 4)
+ || (TYPE_CODE (type) == TYPE_CODE_FLT
+ && TYPE_LENGTH (type) == 8
+ && tdep->wordsize == 4))
+ {
+ /* A long long, or a double stored in r3/r4. */
+ regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3,
+ (bfd_byte *) valbuf + 0);
+ regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 4,
+ (bfd_byte *) valbuf + 4);
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_INT
+ && TYPE_LENGTH (type) <= tdep->wordsize)
+ {
+ /* Some sort of integer stored in r3. Since TYPE isn't bigger
+ than the register, sign extension isn't a problem - just do
+ everything unsigned. */
+ ULONGEST regval;
+ regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
+ ®val);
+ store_unsigned_integer (valbuf, TYPE_LENGTH (type), regval);
+ }
+ else if (TYPE_LENGTH (type) == 16
+ && TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && TYPE_VECTOR (type)
+ && tdep->ppc_vr0_regnum >= 0)
+ {
+ /* Altivec places the return value in "v2". */
+ regcache_cooked_read (regcache, tdep->ppc_vr0_regnum + 2, valbuf);
+ }
+ else if (TYPE_LENGTH (type) == 8
+ && TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && TYPE_VECTOR (type)
+ && tdep->ppc_ev0_regnum >= 0)
+ {
+ /* e500 places the return value in "ev2". */
+ regcache_cooked_read (regcache, tdep->ppc_ev0_regnum + 2, valbuf);
+ }
+ else if (broken && TYPE_LENGTH (type) <= 8)
+ {
+ /* GCC screwed up. The last register isn't "left" aligned.
+ Need to extract the least significant part of each register
+ and then store that. */
+ /* Transfer any full words. */
+ int word = 0;
+ while (1)
+ {
+ ULONGEST reg;
+ int len = TYPE_LENGTH (type) - word * tdep->wordsize;
+ if (len <= 0)
+ break;
+ if (len > tdep->wordsize)
+ len = tdep->wordsize;
+ regcache_cooked_read_unsigned (regcache,
+ tdep->ppc_gp0_regnum + 3 + word,
+ ®);
+ store_unsigned_integer ((bfd_byte *) valbuf + word * tdep->wordsize,
+ len, reg);
+ word++;
+ }
+ }
+ else if (TYPE_LENGTH (type) <= 8)
+ {
+ /* This matches SVr4 PPC, it does not match gcc. */
+ /* The value is padded out to 8 bytes and then loaded, as two
+ "words" into r3/r3. */
+ char regvals[MAX_REGISTER_SIZE * 2];
+ regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3,
+ regvals + 0 * tdep->wordsize);
+ if (TYPE_LENGTH (type) > tdep->wordsize)
+ regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 4,
+ regvals + 1 * tdep->wordsize);
+ memcpy (valbuf, regvals, TYPE_LENGTH (type));
+ }
+ else
+ internal_error (__FILE__, __LINE__, "return type unhandled");
+}
+
+void
+ppc_sysv_abi_extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf)
+{
+ extract_return_value (type, regcache, valbuf, 0);
+}
+
+void
+ppc_sysv_abi_broken_extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf)
+{
+ extract_return_value (type, regcache, valbuf, 1);
+}
+
+static void
+store_return_value (struct type *type, struct regcache *regcache,
+ const void *valbuf, int broken)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ if (using_struct_return (type, 1))
+ error ("Cannot return value");
+ else if (TYPE_CODE (type) == TYPE_CODE_FLT
+ && TYPE_LENGTH (type) <= 8
+ && ppc_floating_point_unit_p (current_gdbarch))
+ {
+ /* Floats and doubles stored in "f1". Convert the value to the
+ register's "double" type. */
+ char regval[MAX_REGISTER_SIZE];
+ struct type *regtype = register_type (current_gdbarch, FP0_REGNUM + 1);
+ convert_typed_floating (valbuf, type, regval, regtype);
+ regcache_cooked_write (regcache, FP0_REGNUM + 1, regval);
+ }
+ else if ((TYPE_CODE (type) == TYPE_CODE_INT
+ && TYPE_LENGTH (type) == 8
+ && tdep->wordsize == 4)
+ || (TYPE_CODE (type) == TYPE_CODE_FLT
+ && TYPE_LENGTH (type) == 8
+ && tdep->wordsize == 4))
+ {
+ /* A long long, or a double stored in r3/r4. */
+ regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3,
+ (bfd_byte *) valbuf + 0);
+ regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 4,
+ (bfd_byte *) valbuf + 4);
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_INT
+ && TYPE_LENGTH (type) <= tdep->wordsize)
+ {
+ /* Some sort of integer stored in r3. Use unpack_long since
+ that should handle any required sign extension. */
+ regcache_cooked_write_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
+ unpack_long (type, valbuf));
+ }
+ else if (TYPE_LENGTH (type) == 16
+ && TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && TYPE_VECTOR (type)
+ && tdep->ppc_vr0_regnum >= 0)
+ {
+ /* Altivec places the return value in "v2". */
+ regcache_cooked_write (regcache, tdep->ppc_vr0_regnum + 2, valbuf);
+ }
+ else if (TYPE_LENGTH (type) == 8
+ && TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && TYPE_VECTOR (type)
+ && tdep->ppc_ev0_regnum >= 0)
+ {
+ /* e500 places the return value in "ev2". */
+ regcache_cooked_write (regcache, tdep->ppc_ev0_regnum + 2, valbuf);
+ }
+ else if (broken && TYPE_LENGTH (type) <= 8)
+ {
+ /* GCC screwed up. The last register isn't "left" aligned.
+ Need to extract the least significant part of each register
+ and then store that. */
+ /* Transfer any full words. */
+ int word = 0;
+ while (1)
+ {
+ ULONGEST reg;
+ int len = TYPE_LENGTH (type) - word * tdep->wordsize;
+ if (len <= 0)
+ break;
+ if (len > tdep->wordsize)
+ len = tdep->wordsize;
+ reg = extract_unsigned_integer ((bfd_byte *) valbuf + word * tdep->wordsize, len);
+ regcache_cooked_write_unsigned (regcache,
+ tdep->ppc_gp0_regnum + 3 + word,
+ reg);
+ word++;
+ }
+ }
+ else if (TYPE_LENGTH (type) <= 8)
+ {
+ /* This matches SVr4 PPC, it does not match gcc. */
+ /* The value is padded out to 8 bytes and then loaded, as two
+ "words" into r3/r3. */
+ char regvals[MAX_REGISTER_SIZE * 2];
+ memset (regvals, 0, sizeof regvals);
+ memcpy (regvals, valbuf, TYPE_LENGTH (type));
+ regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3,
+ regvals + 0 * tdep->wordsize);
+ if (TYPE_LENGTH (type) > tdep->wordsize)
+ regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 4,
+ regvals + 1 * tdep->wordsize);
+ }
+ else
+ internal_error (__FILE__, __LINE__, "return type unhandled");
+}
+
+void
+ppc_sysv_abi_store_return_value (struct type *type, struct regcache *regcache,
+ const void *valbuf)
+{
+ store_return_value (type, regcache, valbuf, 0);
+}
+
+void
+ppc_sysv_abi_broken_store_return_value (struct type *type,
+ struct regcache *regcache,
+ const void *valbuf)
+{
+ store_return_value (type, regcache, valbuf, 1);
+}
Index: ppc-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/ppc-tdep.h,v
retrieving revision 1.18
diff -u -r1.18 ppc-tdep.h
--- ppc-tdep.h 14 Sep 2003 02:04:44 -0000 1.18
+++ ppc-tdep.h 17 Sep 2003 21:22:27 -0000
@@ -33,20 +33,36 @@
int ppc_linux_frameless_function_invocation (struct frame_info *);
void ppc_linux_frame_init_saved_regs (struct frame_info *);
CORE_ADDR ppc_linux_frame_chain (struct frame_info *);
-int ppc_sysv_abi_use_struct_convention (int, struct type *);
-int ppc_sysv_abi_broken_use_struct_convention (int, struct type *);
-CORE_ADDR ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
- CORE_ADDR func_addr,
- struct regcache *regcache,
- CORE_ADDR bp_addr, int nargs,
- struct value **args, CORE_ADDR sp,
- int struct_return,
- CORE_ADDR struct_addr);
int ppc_linux_memory_remove_breakpoint (CORE_ADDR addr, char *contents_cache);
struct link_map_offsets *ppc_linux_svr4_fetch_link_map_offsets (void);
void ppc_linux_supply_gregset (char *buf);
void ppc_linux_supply_fpregset (char *buf);
+
+/* From ppc-sysv-tdep.c... */
+extern CORE_ADDR ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
+ CORE_ADDR func_addr,
+ struct regcache *regcache,
+ CORE_ADDR bp_addr, int nargs,
+ struct value **args,
+ CORE_ADDR sp,
+ int struct_return,
+ CORE_ADDR struct_addr);
+
+extern int ppc_sysv_abi_use_struct_convention (int, struct type *);
+extern void ppc_sysv_abi_store_return_value (struct type *type,
+ struct regcache *regcache,
+ const void *valbuf);
+extern void ppc_sysv_abi_extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf);
+
+extern void ppc_sysv_abi_broken_store_return_value (struct type *type,
+ struct regcache *regcache,
+ const void *valbuf);
+extern void ppc_sysv_abi_broken_extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf);
/* From rs6000-tdep.c... */
CORE_ADDR rs6000_frame_saved_pc (struct frame_info *fi);
Index: ppcnbsd-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppcnbsd-tdep.c,v
retrieving revision 1.8
diff -u -r1.8 ppcnbsd-tdep.c
--- ppcnbsd-tdep.c 17 Sep 2003 20:10:48 -0000 1.8
+++ ppcnbsd-tdep.c 17 Sep 2003 21:22:27 -0000
@@ -204,6 +204,7 @@
return (nbsd_pc_in_sigtramp (pc, func_name));
}
+
/* NetBSD is confused. It appears that 1.5 was using the correct SVr4
convention but, 1.6 switched to the below broken convention. For
the moment use the broken convention. Ulgh!. */
@@ -226,8 +227,11 @@
struct gdbarch *gdbarch)
{
set_gdbarch_pc_in_sigtramp (gdbarch, ppcnbsd_pc_in_sigtramp);
-
+ /* For NetBSD, this is an on again, off again thing. Some systems
+ do use the broken struct convention, and some don't. */
set_gdbarch_use_struct_convention (gdbarch, ppcnbsd_use_struct_convention);
+ set_gdbarch_extract_return_value (gdbarch, ppc_sysv_abi_broken_extract_return_value);
+ set_gdbarch_store_return_value (gdbarch, ppc_sysv_abi_broken_store_return_value);
set_solib_svr4_fetch_link_map_offsets (gdbarch,
nbsd_ilp32_solib_svr4_fetch_link_map_offsets);
}
Index: rs6000-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/rs6000-tdep.c,v
retrieving revision 1.161
diff -u -r1.161 rs6000-tdep.c
--- rs6000-tdep.c 17 Sep 2003 14:24:30 -0000 1.161
+++ rs6000-tdep.c 17 Sep 2003 21:22:28 -0000
@@ -1313,65 +1313,6 @@
return sp;
}
-/* Extract a function return value of type TYPE from raw register array
- REGBUF, and copy that return value into VALBUF in virtual format. */
-static void
-e500_extract_return_value (struct type *valtype, struct regcache *regbuf, void *valbuf)
-{
- int offset = 0;
- int vallen = TYPE_LENGTH (valtype);
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
-
- if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
- && vallen == 8
- && TYPE_VECTOR (valtype))
- {
- regcache_raw_read (regbuf, tdep->ppc_ev0_regnum + 3, valbuf);
- }
- else
- {
- /* Return value is copied starting from r3. Note that r3 for us
- is a pseudo register. */
- int offset = 0;
- int return_regnum = tdep->ppc_gp0_regnum + 3;
- int reg_size = REGISTER_RAW_SIZE (return_regnum);
- int reg_part_size;
- char *val_buffer;
- int copied = 0;
- int i = 0;
-
- /* Compute where we will start storing the value from. */
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
- {
- if (vallen <= reg_size)
- offset = reg_size - vallen;
- else
- offset = reg_size + (reg_size - vallen);
- }
-
- /* How big does the local buffer need to be? */
- if (vallen <= reg_size)
- val_buffer = alloca (reg_size);
- else
- val_buffer = alloca (vallen);
-
- /* Read all we need into our private buffer. We copy it in
- chunks that are as long as one register, never shorter, even
- if the value is smaller than the register. */
- while (copied < vallen)
- {
- reg_part_size = REGISTER_RAW_SIZE (return_regnum + i);
- /* It is a pseudo/cooked register. */
- regcache_cooked_read (regbuf, return_regnum + i,
- val_buffer + copied);
- copied += reg_part_size;
- i++;
- }
- /* Put the stuff in the return buffer. */
- memcpy (valbuf, val_buffer + offset, vallen);
- }
-}
-
/* PowerOpen always puts structures in memory. Vectors, which were
added later, do get returned in a register though. */
@@ -2046,30 +1987,6 @@
return regnum;
}
-/* Write into appropriate registers a function return value
- of type TYPE, given in virtual format. */
-static void
-e500_store_return_value (struct type *type, char *valbuf)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
-
- /* Everything is returned in GPR3 and up. */
- int copied = 0;
- int i = 0;
- int len = TYPE_LENGTH (type);
- while (copied < len)
- {
- int regnum = gdbarch_tdep (current_gdbarch)->ppc_gp0_regnum + 3 + i;
- int reg_size = REGISTER_RAW_SIZE (regnum);
- char *reg_val_buf = alloca (reg_size);
-
- memcpy (reg_val_buf, valbuf + copied, reg_size);
- copied += reg_size;
- deprecated_write_register_gen (regnum, reg_val_buf);
- i++;
- }
-}
-
static void
rs6000_store_return_value (struct type *type, char *valbuf)
{
@@ -2830,9 +2747,6 @@
set_gdbarch_pc_regnum (gdbarch, 64);
set_gdbarch_sp_regnum (gdbarch, 1);
set_gdbarch_deprecated_fp_regnum (gdbarch, 1);
- set_gdbarch_deprecated_extract_return_value (gdbarch,
- rs6000_extract_return_value);
- set_gdbarch_deprecated_store_return_value (gdbarch, rs6000_store_return_value);
if (v->arch == bfd_arch_powerpc)
switch (v->mach)
@@ -2866,8 +2780,6 @@
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, e500_dwarf2_reg_to_regnum);
set_gdbarch_pseudo_register_read (gdbarch, e500_pseudo_register_read);
set_gdbarch_pseudo_register_write (gdbarch, e500_pseudo_register_write);
- set_gdbarch_extract_return_value (gdbarch, e500_extract_return_value);
- set_gdbarch_deprecated_store_return_value (gdbarch, e500_store_return_value);
break;
default:
tdep->ppc_vr0_regnum = -1;
@@ -2965,11 +2877,17 @@
set_gdbarch_frame_args_skip (gdbarch, 8);
if (sysv_abi)
- set_gdbarch_use_struct_convention (gdbarch,
- ppc_sysv_abi_use_struct_convention);
+ {
+ set_gdbarch_use_struct_convention (gdbarch, ppc_sysv_abi_use_struct_convention);
+ set_gdbarch_extract_return_value (gdbarch, ppc_sysv_abi_extract_return_value);
+ set_gdbarch_store_return_value (gdbarch, ppc_sysv_abi_store_return_value);
+ }
else
- set_gdbarch_use_struct_convention (gdbarch,
- rs6000_use_struct_convention);
+ {
+ set_gdbarch_use_struct_convention (gdbarch, rs6000_use_struct_convention);
+ set_gdbarch_deprecated_extract_return_value (gdbarch, rs6000_extract_return_value);
+ set_gdbarch_deprecated_store_return_value (gdbarch, rs6000_store_return_value);
+ }
set_gdbarch_frameless_function_invocation (gdbarch,
rs6000_frameless_function_invocation);