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.
This commit is contained in:
Michael Snyder 2002-08-14 01:20:03 +00:00
parent 32f6f25d26
commit ebafbe8353
2 changed files with 311 additions and 4 deletions

View file

@ -1,3 +1,9 @@
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.
2002-08-13 Andrew Cagney <ac131313@redhat.com>
* vax-tdep.c (vax_get_saved_register): Delete function.

View file

@ -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;