diff options
author | Michael Snyder <msnyder@vmware.com> | 2002-08-14 01:20:03 +0000 |
---|---|---|
committer | Michael Snyder <msnyder@vmware.com> | 2002-08-14 01:20:03 +0000 |
commit | ebafbe8353de91ee76a85f3339f631782a04adc1 (patch) | |
tree | 5e129e3ec8a67e1eb4a49b02e10a58fc67e4cbbc /gdb/mips-tdep.c | |
parent | 32f6f25d26077d885a4d49b26abf2f7ceadbf334 (diff) | |
download | gdb-ebafbe8353de91ee76a85f3339f631782a04adc1.zip gdb-ebafbe8353de91ee76a85f3339f631782a04adc1.tar.gz gdb-ebafbe8353de91ee76a85f3339f631782a04adc1.tar.bz2 |
2002-08-13 Michael Snyder <msnyder@redhat.com>
* mips-tdep.c (mips_o32o64_push_arguments): New function,
cloned from mips_push_arguments, tuned for o32/o64 ABI.
(mips_gdbarch_init): Set gdbarch_push_arguments to new func.
Diffstat (limited to 'gdb/mips-tdep.c')
-rw-r--r-- | gdb/mips-tdep.c | 309 |
1 files changed, 305 insertions, 4 deletions
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c index 421b45f..51c2278 100644 --- a/gdb/mips-tdep.c +++ b/gdb/mips-tdep.c @@ -2591,7 +2591,6 @@ mips_push_arguments (int nargs, argreg, phex (regval, 4)); write_register (argreg++, regval); } - } else { @@ -2644,7 +2643,7 @@ mips_push_arguments (int nargs, register are only written to memory. */ while (len > 0) { - /* Rememer if the argument was written to the stack. */ + /* Remember if the argument was written to the stack. */ int stack_used_p = 0; int partial_len = len < MIPS_SAVED_REGSIZE ? len : MIPS_SAVED_REGSIZE; @@ -2783,6 +2782,8 @@ mips_push_arguments (int nargs, return sp; } +/* N32/N64 version of push_arguments. */ + CORE_ADDR mips_n32n64_push_arguments (int nargs, struct value **args, @@ -3007,6 +3008,306 @@ mips_n32n64_push_arguments (int nargs, return sp; } +/* O32/O64 version of push_arguments. */ + +CORE_ADDR +mips_o32o64_push_arguments (int nargs, + struct value **args, + CORE_ADDR sp, + int struct_return, + CORE_ADDR struct_addr) +{ + int argreg; + int float_argreg; + int argnum; + int len = 0; + int stack_offset = 0; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + /* First ensure that the stack and structure return address (if any) + are properly aligned. The stack has to be at least 64-bit + aligned even on 32-bit machines, because doubles must be 64-bit + aligned. For n32 and n64, stack frames need to be 128-bit + aligned, so we round to this widest known alignment. */ + + sp = ROUND_DOWN (sp, 16); + struct_addr = ROUND_DOWN (struct_addr, 16); + + /* Now make space on the stack for the args. */ + for (argnum = 0; argnum < nargs; argnum++) + len += ROUND_UP (TYPE_LENGTH (VALUE_TYPE (args[argnum])), + MIPS_STACK_ARGSIZE); + sp -= ROUND_UP (len, 16); + + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, + "mips_o32o64_push_arguments: sp=0x%s allocated %d\n", + paddr_nz (sp), ROUND_UP (len, 16)); + + /* Initialize the integer and float register pointers. */ + argreg = A0_REGNUM; + float_argreg = FPA0_REGNUM; + + /* the struct_return pointer occupies the first parameter-passing reg */ + if (struct_return) + { + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, + "mips_o32o64_push_arguments: struct_return reg=%d 0x%s\n", + argreg, paddr_nz (struct_addr)); + write_register (argreg++, struct_addr); + stack_offset += MIPS_STACK_ARGSIZE; + } + + /* Now load as many as possible of the first arguments into + registers, and push the rest onto the stack. Loop thru args + from first to last. */ + for (argnum = 0; argnum < nargs; argnum++) + { + char *val; + char *valbuf = alloca (MAX_REGISTER_RAW_SIZE); + struct value *arg = args[argnum]; + struct type *arg_type = check_typedef (VALUE_TYPE (arg)); + int len = TYPE_LENGTH (arg_type); + enum type_code typecode = TYPE_CODE (arg_type); + + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, + "mips_o32o64_push_arguments: %d len=%d type=%d", + argnum + 1, len, (int) typecode); + + val = (char *) VALUE_CONTENTS (arg); + + /* 32-bit ABIs always start floating point arguments in an + even-numbered floating point register. Round the FP register + up before the check to see if there are any FP registers + left. O32/O64 targets also pass the FP in the integer + registers so also round up normal registers. */ + if (!FP_REGISTER_DOUBLE + && fp_register_arg_p (typecode, arg_type)) + { + if ((float_argreg & 1)) + float_argreg++; + } + + /* Floating point arguments passed in registers have to be + treated specially. On 32-bit architectures, doubles + are passed in register pairs; the even register gets + the low word, and the odd register gets the high word. + On O32/O64, the first two floating point arguments are + also copied to general registers, because MIPS16 functions + don't use float registers for arguments. This duplication of + arguments in general registers can't hurt non-MIPS16 functions + because those registers are normally skipped. */ + + if (fp_register_arg_p (typecode, arg_type) + && float_argreg <= MIPS_LAST_FP_ARG_REGNUM) + { + if (!FP_REGISTER_DOUBLE && len == 8) + { + int low_offset = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG ? 4 : 0; + unsigned long regval; + + /* Write the low word of the double to the even register(s). */ + regval = extract_unsigned_integer (val + low_offset, 4); + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", + float_argreg, phex (regval, 4)); + write_register (float_argreg++, regval); + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", + argreg, phex (regval, 4)); + write_register (argreg++, regval); + + /* Write the high word of the double to the odd register(s). */ + regval = extract_unsigned_integer (val + 4 - low_offset, 4); + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", + float_argreg, phex (regval, 4)); + write_register (float_argreg++, regval); + + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", + argreg, phex (regval, 4)); + write_register (argreg++, regval); + } + else + { + /* This is a floating point value that fits entirely + in a single register. */ + /* On 32 bit ABI's the float_argreg is further adjusted + above to ensure that it is even register aligned. */ + LONGEST regval = extract_unsigned_integer (val, len); + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", + float_argreg, phex (regval, len)); + write_register (float_argreg++, regval); + /* CAGNEY: 32 bit MIPS ABI's always reserve two FP + registers for each argument. The below is (my + guess) to ensure that the corresponding integer + register has reserved the same space. */ + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", + argreg, phex (regval, len)); + write_register (argreg, regval); + argreg += FP_REGISTER_DOUBLE ? 1 : 2; + } + /* Reserve space for the FP register. */ + stack_offset += ROUND_UP (len, MIPS_STACK_ARGSIZE); + } + else + { + /* Copy the argument to general registers or the stack in + register-sized pieces. Large arguments are split between + registers and stack. */ + /* Note: structs whose size is not a multiple of MIPS_REGSIZE + are treated specially: Irix cc passes them in registers + where gcc sometimes puts them on the stack. For maximum + compatibility, we will put them in both places. */ + int odd_sized_struct = ((len > MIPS_SAVED_REGSIZE) && + (len % MIPS_SAVED_REGSIZE != 0)); + /* Structures should be aligned to eight bytes (even arg registers) + on MIPS_ABI_O32, if their first member has double precision. */ + if (MIPS_SAVED_REGSIZE < 8 + && mips_type_needs_double_align (arg_type)) + { + if ((argreg & 1)) + argreg++; + } + /* Note: Floating-point values that didn't fit into an FP + register are only written to memory. */ + while (len > 0) + { + /* Remember if the argument was written to the stack. */ + int stack_used_p = 0; + int partial_len = + len < MIPS_SAVED_REGSIZE ? len : MIPS_SAVED_REGSIZE; + + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, " -- partial=%d", + partial_len); + + /* Write this portion of the argument to the stack. */ + if (argreg > MIPS_LAST_ARG_REGNUM + || odd_sized_struct + || fp_register_arg_p (typecode, arg_type)) + { + /* Should shorter than int integer values be + promoted to int before being stored? */ + int longword_offset = 0; + CORE_ADDR addr; + stack_used_p = 1; + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + { + if (MIPS_STACK_ARGSIZE == 8 && + (typecode == TYPE_CODE_INT || + typecode == TYPE_CODE_PTR || + typecode == TYPE_CODE_FLT) && len <= 4) + longword_offset = MIPS_STACK_ARGSIZE - len; + } + + if (mips_debug) + { + fprintf_unfiltered (gdb_stdlog, " - stack_offset=0x%s", + paddr_nz (stack_offset)); + fprintf_unfiltered (gdb_stdlog, " longword_offset=0x%s", + paddr_nz (longword_offset)); + } + + addr = sp + stack_offset + longword_offset; + + if (mips_debug) + { + int i; + fprintf_unfiltered (gdb_stdlog, " @0x%s ", + paddr_nz (addr)); + for (i = 0; i < partial_len; i++) + { + fprintf_unfiltered (gdb_stdlog, "%02x", + val[i] & 0xff); + } + } + write_memory (addr, val, partial_len); + } + + /* Note!!! This is NOT an else clause. Odd sized + structs may go thru BOTH paths. Floating point + arguments will not. */ + /* Write this portion of the argument to a general + purpose register. */ + if (argreg <= MIPS_LAST_ARG_REGNUM + && !fp_register_arg_p (typecode, arg_type)) + { + LONGEST regval = extract_signed_integer (val, partial_len); + /* Value may need to be sign extended, because + MIPS_REGSIZE != MIPS_SAVED_REGSIZE. */ + + /* A non-floating-point argument being passed in a + general register. If a struct or union, and if + the remaining length is smaller than the register + size, we have to adjust the register value on + big endian targets. + + It does not seem to be necessary to do the + same for integral types. + + Also don't do this adjustment on O64 binaries. + + cagney/2001-07-23: gdb/179: Also, GCC, when + outputting LE O32 with sizeof (struct) < + MIPS_SAVED_REGSIZE, generates a left shift as + part of storing the argument in a register a + register (the left shift isn't generated when + sizeof (struct) >= MIPS_SAVED_REGSIZE). Since it + is quite possible that this is GCC contradicting + the LE/O32 ABI, GDB has not been adjusted to + accommodate this. Either someone needs to + demonstrate that the LE/O32 ABI specifies such a + left shift OR this new ABI gets identified as + such and GDB gets tweaked accordingly. */ + + if (MIPS_SAVED_REGSIZE < 8 + && TARGET_BYTE_ORDER == BFD_ENDIAN_BIG + && partial_len < MIPS_SAVED_REGSIZE + && (typecode == TYPE_CODE_STRUCT || + typecode == TYPE_CODE_UNION)) + regval <<= ((MIPS_SAVED_REGSIZE - partial_len) * + TARGET_CHAR_BIT); + + if (mips_debug) + fprintf_filtered (gdb_stdlog, " - reg=%d val=%s", + argreg, + phex (regval, MIPS_SAVED_REGSIZE)); + write_register (argreg, regval); + argreg++; + + /* Prevent subsequent floating point arguments from + being passed in floating point registers. */ + float_argreg = MIPS_LAST_FP_ARG_REGNUM + 1; + } + + len -= partial_len; + val += partial_len; + + /* Compute the the offset into the stack at which we + will copy the next parameter. + + In older ABIs, the caller reserved space for + registers that contained arguments. This was loosely + refered to as their "home". Consequently, space is + always allocated. */ + + stack_offset += ROUND_UP (partial_len, MIPS_STACK_ARGSIZE); + } + } + if (mips_debug) + fprintf_unfiltered (gdb_stdlog, "\n"); + } + + /* Return adjusted stack pointer. */ + return sp; +} + CORE_ADDR mips_push_return_address (CORE_ADDR pc, CORE_ADDR sp) { @@ -4775,7 +5076,7 @@ mips_gdbarch_init (struct gdbarch_info info, switch (mips_abi) { case MIPS_ABI_O32: - set_gdbarch_push_arguments (gdbarch, mips_push_arguments); + set_gdbarch_push_arguments (gdbarch, mips_o32o64_push_arguments); tdep->mips_default_saved_regsize = 4; tdep->mips_default_stack_argsize = 4; tdep->mips_fp_register_double = 0; @@ -4793,7 +5094,7 @@ mips_gdbarch_init (struct gdbarch_info info, mips_o32_use_struct_convention); break; case MIPS_ABI_O64: - set_gdbarch_push_arguments (gdbarch, mips_push_arguments); + set_gdbarch_push_arguments (gdbarch, mips_o32o64_push_arguments); tdep->mips_default_saved_regsize = 8; tdep->mips_default_stack_argsize = 8; tdep->mips_fp_register_double = 1; |