diff options
-rw-r--r-- | gdb/ChangeLog | 22 | ||||
-rw-r--r-- | gdb/ppc-sysv-tdep.c | 272 | ||||
-rw-r--r-- | gdb/ppc-tdep.h | 13 | ||||
-rw-r--r-- | gdb/ppcnbsd-tdep.c | 5 | ||||
-rw-r--r-- | gdb/rs6000-tdep.c | 90 |
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, + ®val); + 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, + ®); + 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; |