summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog22
-rw-r--r--gdb/ppc-sysv-tdep.c272
-rw-r--r--gdb/ppc-tdep.h13
-rw-r--r--gdb/ppcnbsd-tdep.c5
-rw-r--r--gdb/rs6000-tdep.c90
5 files changed, 294 insertions, 108 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 540368b1bcf..a88bce51a06 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,25 @@
+2003-10-10 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: (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_abi_broken_use_struct_convention:) Delete declaration.
+ * ppc-sysv-tdep.c (return_value_convention): Move definition to
+ start of file.
+ (do_ppc_sysv_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.
+ (ppc_sysv_abi_use_struct_convention): Call
+ do_ppc_sysv_return_value.
+
2003-10-10 J. Brobecker <brobecker@gnat.com>
* blockframe.c (inside_main_func): No longer use symbol_lookup()
diff --git a/gdb/ppc-sysv-tdep.c b/gdb/ppc-sysv-tdep.c
index 0b153661e81..8091c2f56c2 100644
--- a/gdb/ppc-sysv-tdep.c
+++ b/gdb/ppc-sysv-tdep.c
@@ -305,16 +305,262 @@ ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr,
return sp;
}
+/* Potential ways that a function can return a value of a given type. */
+enum return_value_convention
+{
+ /* Where the return value has been squeezed into one or more
+ registers. */
+ RETURN_VALUE_REGISTER_CONVENTION,
+ /* Commonly known as the "struct return convention". The caller
+ passes an additional hidden first parameter to the caller. That
+ parameter contains the address at which the value being returned
+ should be stored. While typically, and historically, used for
+ large structs, this is convention is applied to values of many
+ different types. */
+ RETURN_VALUE_STRUCT_CONVENTION
+};
+
+/* Handle the return-value conventions specified by the SysV 32-bit
+ PowerPC ABI (including all the supplements):
+
+ no floating-point: floating-point values returned using 32-bit
+ general-purpose registers.
+
+ Altivec: 128-bit vectors returned using vector registers.
+
+ e500: 64-bit vectors returned using the full full 64 bit EV
+ register, floating-point values returned using 32-bit
+ general-purpose registers.
+
+ GCC (broken): Small struct values right (instead of left) aligned
+ when returned in general-purpose registers. */
+
+static enum return_value_convention
+do_ppc_sysv_return_value (struct type *type, struct regcache *regcache,
+ const void *inval, void *outval, int broken_gcc)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ gdb_assert (tdep->wordsize == 4);
+ if (TYPE_CODE (type) == TYPE_CODE_FLT
+ && TYPE_LENGTH (type) <= 8
+ && ppc_floating_point_unit_p (current_gdbarch))
+ {
+ if (outval)
+ {
+ /* 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, outval, type);
+ }
+ if (inval)
+ {
+ /* 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);
+ convert_typed_floating (inval, type, regval, regtype);
+ regcache_cooked_write (regcache, FP0_REGNUM + 1, regval);
+ }
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
+ if ((TYPE_CODE (type) == TYPE_CODE_INT && TYPE_LENGTH (type) == 8)
+ || (TYPE_CODE (type) == TYPE_CODE_FLT && TYPE_LENGTH (type) == 8))
+ {
+ if (outval)
+ {
+ /* A long long, or a double stored in the 32 bit r3/r4. */
+ regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3,
+ (bfd_byte *) outval + 0);
+ regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 4,
+ (bfd_byte *) outval + 4);
+ }
+ if (inval)
+ {
+ /* A long long, or a double stored in the 32 bit r3/r4. */
+ regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3,
+ (bfd_byte *) inval + 0);
+ regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 4,
+ (bfd_byte *) inval + 4);
+ }
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
+ if (TYPE_CODE (type) == TYPE_CODE_INT
+ && TYPE_LENGTH (type) <= tdep->wordsize)
+ {
+ if (outval)
+ {
+ /* 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,
+ &regval);
+ store_unsigned_integer (outval, TYPE_LENGTH (type), regval);
+ }
+ if (inval)
+ {
+ /* 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, inval));
+ }
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
+ if (TYPE_LENGTH (type) == 16
+ && TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && TYPE_VECTOR (type) && tdep->ppc_vr0_regnum >= 0)
+ {
+ if (outval)
+ {
+ /* Altivec places the return value in "v2". */
+ regcache_cooked_read (regcache, tdep->ppc_vr0_regnum + 2, outval);
+ }
+ if (inval)
+ {
+ /* Altivec places the return value in "v2". */
+ regcache_cooked_write (regcache, tdep->ppc_vr0_regnum + 2, inval);
+ }
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
+ if (TYPE_LENGTH (type) == 8
+ && TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && TYPE_VECTOR (type) && tdep->ppc_ev0_regnum >= 0)
+ {
+ /* The e500 ABI places return values for the 64-bit DSP types
+ (__ev64_opaque__) in r3. However, in GDB-speak, ev3
+ corresponds to the entire r3 value for e500, whereas GDB's r3
+ only corresponds to the least significant 32-bits. So place
+ the 64-bit DSP type's value in ev3. */
+ if (outval)
+ regcache_cooked_read (regcache, tdep->ppc_ev0_regnum + 3, outval);
+ if (inval)
+ regcache_cooked_write (regcache, tdep->ppc_ev0_regnum + 3, inval);
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
+ if (broken_gcc && TYPE_LENGTH (type) <= 8)
+ {
+ if (outval)
+ {
+ /* 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,
+ &reg);
+ store_unsigned_integer (((bfd_byte *) outval
+ + word * tdep->wordsize), len, reg);
+ word++;
+ }
+ }
+ if (inval)
+ {
+ /* 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 *) inval
+ + word * tdep->wordsize), len);
+ regcache_cooked_write_unsigned (regcache,
+ tdep->ppc_gp0_regnum + 3 + word,
+ reg);
+ word++;
+ }
+ }
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
+ if (TYPE_LENGTH (type) <= 8)
+ {
+ if (outval)
+ {
+ /* This matches SVr4 PPC, it does not match GCC. */
+ /* The value is right-padded to 8 bytes and then loaded, as
+ two "words", into r3/r4. */
+ 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 (outval, regvals, TYPE_LENGTH (type));
+ }
+ if (inval)
+ {
+ /* 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/r4. */
+ char regvals[MAX_REGISTER_SIZE * 2];
+ memset (regvals, 0, sizeof regvals);
+ memcpy (regvals, inval, 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);
+ }
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
+ return RETURN_VALUE_STRUCT_CONVENTION;
+}
+
+void
+ppc_sysv_abi_extract_return_value (struct type *type,
+ struct regcache *regcache, void *valbuf)
+{
+ do_ppc_sysv_return_value (type, regcache, NULL, valbuf, 0);
+}
+
+void
+ppc_sysv_abi_broken_extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf)
+{
+ do_ppc_sysv_return_value (type, regcache, NULL, valbuf, 1);
+}
+
+void
+ppc_sysv_abi_store_return_value (struct type *type, struct regcache *regcache,
+ const void *valbuf)
+{
+ do_ppc_sysv_return_value (type, regcache, valbuf, NULL, 0);
+}
+
+void
+ppc_sysv_abi_broken_store_return_value (struct type *type,
+ struct regcache *regcache,
+ const void *valbuf)
+{
+ do_ppc_sysv_return_value (type, regcache, valbuf, NULL, 1);
+}
+
/* Structures 8 bytes or less long are returned in the r3 & r4
registers, according to the SYSV ABI. */
int
ppc_sysv_abi_use_struct_convention (int gcc_p, struct type *value_type)
{
- if ((TYPE_LENGTH (value_type) == 16 || TYPE_LENGTH (value_type) == 8)
- && TYPE_VECTOR (value_type))
- return 0;
-
- return (TYPE_LENGTH (value_type) > 8);
+ return (do_ppc_sysv_return_value (value_type, NULL, NULL, NULL, 0)
+ == RETURN_VALUE_STRUCT_CONVENTION);
}
/* Pass the arguments in either registers, or in the stack. Using the
@@ -617,22 +863,6 @@ ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr,
copy the buffer to the corresponding register return-value location
location; when OUTVAL is non-NULL, fill the buffer from the
corresponding register return-value location. */
-
-/* Potential ways that a function can return a value of a given type. */
-enum return_value_convention
-{
- /* Where the return value has been squeezed into one or more
- registers. */
- RETURN_VALUE_REGISTER_CONVENTION,
- /* Commonly known as the "struct return convention". The caller
- passes an additional hidden first parameter to the caller. That
- parameter contains the address at which the value being returned
- should be stored. While typically, and historically, used for
- large structs, this is convention is applied to values of many
- different types. */
- RETURN_VALUE_STRUCT_CONVENTION
-};
-
static enum return_value_convention
ppc64_sysv_abi_return_value (struct type *valtype, struct regcache *regcache,
const void *inval, void *outval)
diff --git a/gdb/ppc-tdep.h b/gdb/ppc-tdep.h
index 4e6788c9b3b..eb615829510 100644
--- a/gdb/ppc-tdep.h
+++ b/gdb/ppc-tdep.h
@@ -35,7 +35,18 @@ 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 *);
+void ppc_sysv_abi_store_return_value (struct type *type,
+ struct regcache *regcache,
+ const void *valbuf);
+void ppc_sysv_abi_extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf);
+void ppc_sysv_abi_broken_store_return_value (struct type *type,
+ struct regcache *regcache,
+ const void *valbuf);
+void ppc_sysv_abi_broken_extract_return_value (struct type *type,
+ struct regcache *regcache,
+ void *valbuf);
CORE_ADDR ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
CORE_ADDR func_addr,
struct regcache *regcache,
diff --git a/gdb/ppcnbsd-tdep.c b/gdb/ppcnbsd-tdep.c
index 97323d57249..b566f30e4f7 100644
--- a/gdb/ppcnbsd-tdep.c
+++ b/gdb/ppcnbsd-tdep.c
@@ -226,8 +226,11 @@ ppcnbsd_init_abi (struct gdbarch_info info,
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);
}
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 3a0d4830300..98ad98b7708 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -1313,65 +1313,6 @@ ran_out_of_registers_for_arguments:
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 = DEPRECATED_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 = DEPRECATED_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 @@ rs6000_stab_reg_to_regnum (int num)
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 = DEPRECATED_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)
{
@@ -2835,6 +2752,11 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_extract_return_value (gdbarch, ppc64_sysv_abi_extract_return_value);
set_gdbarch_store_return_value (gdbarch, ppc64_sysv_abi_store_return_value);
}
+ else if (sysv_abi && wordsize == 4)
+ {
+ 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_deprecated_extract_return_value (gdbarch, rs6000_extract_return_value);
@@ -2873,8 +2795,6 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
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;