From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24218 invoked by alias); 20 Apr 2002 09:50:09 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 24205 invoked from network); 20 Apr 2002 09:50:04 -0000 Received: from unknown (HELO pizda.ninka.net) (216.101.162.242) by sources.redhat.com with SMTP; 20 Apr 2002 09:50:04 -0000 Received: from localhost (IDENT:davem@localhost.localdomain [127.0.0.1]) by pizda.ninka.net (8.9.3/8.9.3) with ESMTP id CAA10977 for ; Sat, 20 Apr 2002 02:41:22 -0700 Date: Sat, 20 Apr 2002 02:50:00 -0000 Message-Id: <20020420.024122.73287574.davem@redhat.com> To: gdb-patches@sources.redhat.com Subject: [RFA] Fix sparc64 argument passing From: "David S. Miller" Mime-Version: 1.0 Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-SW-Source: 2002-04/txt/msg00677.txt.bz2 Instead of trying to fixup the code there, I reimplemented it using the Sparc GCC backend code for argument passing as a template. Advantages: 1) It works 2) Sparc32 support can be folded into it 3) Matching code means fixes in one can propagate (hopefully) to the other. :-) Regression update: sparc32 sparc64 failures before 83 110 failures after 83 105 2002-04-20 David S. Miller Fix sparc64 call dummy argument passing. * sparc-tdep.c: Include gdb_assert.h (sparc32_push_arguments): Allocate minimum of 6 arg slots so that varargs doesn't write all over the stack. (sparc_fix_call_dummy): Note that V9/ARCH64 do not have the unimp after call convention. (SPARC_INT_ARG_MAX, SPARC_FP_ARG_MAX, INTEGER_P): Define. (sp64_compute_arg_slotno, sp64_arg_advance, sp64_compute_arg_stack_space, sp64_struct_regs_analyze, sp64_do_struct_regs_3, sp64_do_struct_regs_2, sp64_push_one_struct_regs, sp64_push_one_arg): New functions. (sparc64_push_arguments): Reimplement from scratch. (sp64_extract_return_value): Handle TYPE_CODE_COMPLEX. --- sparc-tdep.c.~1~ Sat Apr 20 02:13:23 2002 +++ sparc-tdep.c Sat Apr 20 02:22:28 2002 @@ -32,6 +32,7 @@ #include "value.h" #include "bfd.h" #include "gdb_string.h" +#include "gdb_assert.h" #include "regcache.h" #ifdef USE_PROC_FS @@ -2070,6 +2071,12 @@ sparc32_push_arguments (int nargs, struc m_arg->contents = VALUE_CONTENTS (arg); } + /* Make room for at least 6 integer arguments for the sake of + varargs. Otherwise a varargs function, once called via + a CALL_DUMMY, can write over other parts of the stack. */ + if (accumulate_size < (6 * 4)) + accumulate_size = (6 * 4); + /* Make room for the arguments on the stack. */ accumulate_size += CALL_DUMMY_STACK_ADJUST; sp = ((sp - accumulate_size) & ~7) + CALL_DUMMY_STACK_ADJUST; @@ -2186,6 +2193,9 @@ sparc_fix_call_dummy (char *dummy, CORE_ which follows the call instruction. For details see the SPARC Architecture Manual Version 8, Appendix D.3. + This does not happen on V9/ARCH64, such structure returns are passed + by reference. + Adjust the call_dummy_breakpoint_offset for the bp_call_dummy breakpoint to the proper address in the call dummy, so that `finish' after a stop in a call dummy works. @@ -2303,131 +2313,578 @@ sparc64_write_sp (CORE_ADDR val) write_register (SP_REGNUM, val); } -/* The SPARC 64 ABI passes floating-point arguments in FP0 to FP31, - and all other arguments in O0 to O5. They are also copied onto - the stack in the correct places. Apparently (empirically), - structs of less than 16 bytes are passed member-by-member in - separate registers, but I am unable to figure out the algorithm. - Some members go in floating point regs, but I don't know which. - - FIXME: Handle small structs (less than 16 bytes containing floats). - - The counting regimen for using both integer and FP registers - for argument passing is rather odd -- a single counter is used - for both; this means that if the arguments alternate between - int and float, we will waste every other register of both types. */ +/* This is basically extracted from the Sparc backend of GCC. + All errors introduced along the way are mine. -DaveM */ + +#define SPARC_INT_ARG_MAX 6 +#define SPARC_FP_ARG_MAX 16 + +#define INTEGER_P(TYPE) \ + ((TYPE) == TYPE_CODE_INT || (TYPE) == TYPE_CODE_BOOL \ + || (TYPE) == TYPE_CODE_CHAR || (TYPE) == TYPE_CODE_RANGE \ + || (TYPE) == TYPE_CODE_ENUM || (TYPE) == TYPE_CODE_PTR \ + || (TYPE) == TYPE_CODE_REF) + +static int +sp64_compute_arg_slotno (int words, int *ppadding, int *pregno, + struct value *val, enum type_code type, int len) + { + int regbase = O0_REGNUM; + int slotno = words; + int regno; + + *ppadding = 0; + + switch (type) + { + case TYPE_CODE_UNDEF: + case TYPE_CODE_ARRAY: + case TYPE_CODE_FUNC: + case TYPE_CODE_VOID: + case TYPE_CODE_ERROR: + case TYPE_CODE_MEMBER: + case TYPE_CODE_METHOD: + case TYPE_CODE_SET: + case TYPE_CODE_STRING: + case TYPE_CODE_BITSTRING: + case TYPE_CODE_TYPEDEF: + case TYPE_CODE_TEMPLATE: + case TYPE_CODE_TEMPLATE_ARG: + /* If we see any of these, just bail... */ + error ("Type cannot be pushed into argument slot by GDB target code.\n"); + + default: + break; + }; + + if (INTEGER_P (type)) + { + if (slotno >= SPARC_INT_ARG_MAX) + { + return -1; + } + regno = regbase + slotno; + } + else if (type == TYPE_CODE_FLT + || type == TYPE_CODE_COMPLEX) + { + if (len == 16) + { + /* TFmode value. */ + gdb_assert(SPARC_TARGET_LONG_DOUBLE_BYTES == 16 + || type == TYPE_CODE_COMPLEX); + if ((slotno & 1) != 0) + { + slotno++; + *ppadding = 1; + } + } + if (SPARC_HAS_FPU) + { + if (slotno >= SPARC_FP_ARG_MAX) + { + return -1; + } + regno = FP0_REGNUM + slotno * 2; + if (len == 4) + { + /* SFmode value. */ + regno++; + } + } + else + { + /* Passed in integer registers, if possible. */ + if (slotno >= SPARC_INT_ARG_MAX) + { + return -1; + } + regno = regbase + slotno; + } + } + else + { + /* A structure or a union. */ + + /* ??? No way to get at required alignment of types. + ??? We are supposed to give 16byte alignment to types + ??? that require it (via attribute((align)) or similar) + ??? but GDB appears to have no way to get at that information + ??? currently. */ +#if 0 + if (TYPE_ALIGN (VALUE_TYPE (val)) == 128 + && (slotno & 1) != 0) + { + slotno++; + *ppadding = 1; + } +#endif + + if (type == TYPE_CODE_UNION + || len > 16) + { + if (slotno >= SPARC_INT_ARG_MAX) + { + return -1; + } + regno = regbase + slotno; + } + else + { + struct type *tp = VALUE_TYPE (val); + int intregs_p = 0, fpregs_p = 0; + int packed_p = 0; + int field; + + gdb_assert (type == TYPE_CODE_STRUCT); + + for (field = 0; field < tp->nfields; field++) + { + struct field *fp = &tp->fields[field]; + + if ((TYPE_CODE (fp->type) == TYPE_CODE_FLT + || TYPE_CODE (fp->type) == TYPE_CODE_COMPLEX) + && SPARC_HAS_FPU) + fpregs_p = 1; + else + intregs_p = 1; + + /* ??? No way to get at this currently... */ +#if 0 + if (TYPE_PACKED (fp->type)) + packed_p = 1; +#endif + } + + if (packed_p) + { + fpregs_p = 0; + intregs_p = 1; + } + + if (fpregs_p && slotno >= SPARC_FP_ARG_MAX) + { + return -1; + } + + if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX) + { + return -1; + } + + return slotno; + } + } + + *pregno = regno; + return slotno; +} + +static void +sp64_arg_advance (int *p_arg_words, struct value *val) +{ + enum type_code type = TYPE_CODE (VALUE_TYPE (val)); + int len = TYPE_LENGTH (check_typedef (VALUE_TYPE (val))); + int slotno, regno, padding; + + slotno = sp64_compute_arg_slotno (*p_arg_words, &padding, ®no, + val, type, len); + + if (slotno != -1) + *p_arg_words += padding; + + if (type == TYPE_CODE_UNION + || type == TYPE_CODE_STRUCT) + { + gdb_assert (len <= 16); + + if (len <= 8) + *p_arg_words += 1; + else if (len <= 16) + *p_arg_words += 2; + } +#if 0 /* ??? How to do this */ + else if (type == TYPE_CODE_COMPLEX_INT) + { + *p_arg_words += 2; + } + else if (type == TYPE_CODE_COMPLEX_FLOAT) + { + *p_arg_words += len / SPARC_INTREG_SIZE; + } +#endif + else + { + *p_arg_words += ((len + (SPARC_INTREG_SIZE - 1)) + / SPARC_INTREG_SIZE); + } +} + +static void +sp64_compute_arg_stack_space (int *p_arg_words, + int nargs, struct value **args, int struct_return) +{ + int i; + + *p_arg_words = 0; + + /* This is true if USING_STRUCT_CONVENTION, in which case the function + is returning a structure larger than 32 bytes and causes the first + argument slot to be a pointer to the allocated space for it. */ + if (struct_return) + *p_arg_words += 1; + + for (i = 0; i < nargs; i++) + { + struct value *val = args[i]; + + sp64_arg_advance (p_arg_words, val); + } +} + +struct sp64_struct_regs_state +{ + int slotno; + unsigned int nregs; + int intoffset; +}; + +static void +sp64_struct_regs_analyze (struct type *tp, + struct sp64_struct_regs_state *statep) +{ + int field; + + for (field = 0; field < tp->nfields; field++) + { + struct field *fp = &tp->fields[field]; + int bitpos = fp->loc.bitpos; + int this_len = TYPE_LENGTH (check_typedef (fp->type)); + + if (this_len == 0) + bitpos = 0; + + if (TYPE_CODE (fp->type) == TYPE_CODE_STRUCT) + sp64_struct_regs_analyze (fp->type, statep); + else if ((TYPE_CODE (fp->type) == TYPE_CODE_FLT + || TYPE_CODE (fp->type) == TYPE_CODE_COMPLEX) + && SPARC_HAS_FPU) + { + if (statep->intoffset != -1) + { + int intslots, this_slotno; + int bits_per_word = SPARC_INTREG_SIZE * 8; + + intslots = (bitpos - statep->intoffset + (bits_per_word - 1)) + / bits_per_word; + this_slotno = statep->slotno + + (statep->intoffset / bits_per_word); + + if (SPARC_INT_ARG_MAX - this_slotno < intslots) + intslots = SPARC_INT_ARG_MAX - this_slotno; + if (intslots < 0) + intslots = 0; + statep->nregs += intslots; + statep->intoffset = -1; + } + + statep->nregs += 1; + if (TYPE_CODE (fp->type) == TYPE_CODE_COMPLEX) + statep->nregs += 1; + } + else + { + if (statep->intoffset == -1) + statep->intoffset = bitpos; + } + } +} + +static void +sp64_do_struct_regs_3 (struct value *val, int bitpos, + struct sp64_struct_regs_state *statep) +{ + int regno, startbit, endbit, this_slotno, intslots, intoffset; + int bits_per_word = (SPARC_INTREG_SIZE * 8); + char *zero_buf; + + if (statep->intoffset == -1) + return; + + intoffset = statep->intoffset; + statep->intoffset = -1; + + startbit = intoffset & -bits_per_word; + endbit = (bitpos + (bits_per_word - 1)) & -bits_per_word; + intslots = (endbit - startbit) / bits_per_word; + this_slotno = statep->slotno + (intoffset / bits_per_word); + + if (SPARC_INT_ARG_MAX - this_slotno < intslots) + intslots = SPARC_INT_ARG_MAX - this_slotno; + if (intslots <= 0) + return; + + zero_buf = alloca(SPARC_INTREG_SIZE); + memset (zero_buf, 0, sizeof (zero_buf)); + + intoffset /= 8; + do + { + regno = O0_REGNUM + this_slotno; + + if (intoffset % SPARC_INTREG_SIZE) + { + write_register_gen (regno, zero_buf); + write_register_bytes (REGISTER_BYTE (regno) + + (intoffset % SPARC_INTREG_SIZE), + VALUE_CONTENTS (val) + intoffset, + SPARC_INTREG_SIZE + - (intoffset % SPARC_INTREG_SIZE)); + } + else + { + write_register_gen (regno, VALUE_CONTENTS (val) + intoffset); + } + + this_slotno += 1; + intoffset = (intoffset | (SPARC_INTREG_SIZE - 1)) + 1; + statep->nregs += 1; + intslots -= 1; + } + while (intslots > 0); +} + +static void +sp64_do_struct_regs_2 (struct value *val, struct type *tp, + struct sp64_struct_regs_state *statep) +{ + int field; + + for (field = 0; field < tp->nfields; field++) + { + struct field *fp = &tp->fields[field]; + int bitpos = fp->loc.bitpos; + int this_len = TYPE_LENGTH (check_typedef (fp->type)); + + if (this_len == 0) + bitpos = 0; + + if (TYPE_CODE (fp->type) == TYPE_CODE_STRUCT) + sp64_do_struct_regs_2 (val, fp->type, statep); + else if ((TYPE_CODE (fp->type) == TYPE_CODE_FLT + || TYPE_CODE (fp->type) == TYPE_CODE_COMPLEX) + && SPARC_HAS_FPU) + { + int bits_per_word = SPARC_INTREG_SIZE * 8; + int this_slotno = statep->slotno + (bitpos / bits_per_word); + int this_typelen = TYPE_LENGTH (check_typedef (fp->type)); + int regno; + + sp64_do_struct_regs_3 (val, bitpos, statep); + regno = FP0_REGNUM + this_slotno * 2 + + (this_typelen == 4 && (bitpos & 32) != 0); + + write_register_bytes (REGISTER_BYTE (regno), + VALUE_CONTENTS (val) + (bitpos / 8), + this_typelen); + statep->nregs += 1; + if (TYPE_CODE (fp->type) == TYPE_CODE_COMPLEX) + statep->nregs += 1; + } + else + { + if (statep->intoffset == -1) + statep->intoffset = bitpos; + } + } +} + +static void +sp64_push_one_struct_regs (int slotno, + struct value *val, enum type_code type, int len) +{ + struct sp64_struct_regs_state state; + int nregs; + + state.slotno = slotno; + + state.nregs = 0; + state.intoffset = 0; + sp64_struct_regs_analyze (VALUE_TYPE (val), &state); + + if (state.intoffset != -1) + { + unsigned int startbit, endbit; + int intslots, this_slotno; + int bits_per_word; + + bits_per_word = SPARC_INTREG_SIZE * 8; + + startbit = state.intoffset & -bits_per_word; + endbit = (len * 8 + (bits_per_word - 1)) & -bits_per_word; + intslots = (endbit - startbit) / bits_per_word; + this_slotno = slotno + state.intoffset / bits_per_word; + + if (SPARC_INT_ARG_MAX - this_slotno < intslots) + intslots = SPARC_INT_ARG_MAX - this_slotno; + if (intslots < 0) + intslots = 0; + + state.nregs += intslots; + } + nregs = state.nregs; + + if (nregs == 0) + { + /* Nothing can actually go in the registers, oh well... */ + return; + } + + state.nregs = 0; + state.intoffset = 0; + sp64_do_struct_regs_2 (val, VALUE_TYPE (val), &state); + sp64_do_struct_regs_3 (val, len * 8, &state); + gdb_assert (state.nregs == nregs); +} + +static void +sp64_push_one_arg (CORE_ADDR *sp, CORE_ADDR args_base, + struct value *val, enum type_code type, int len) +{ + int cur_word = (*sp - args_base) / SPARC_INTREG_SIZE; + int slotno, padding, regno, dummy; + int orig_cur_word; + + slotno = sp64_compute_arg_slotno (cur_word, &padding, ®no, + val, type, len); + + /* If integral, it may need promotion. */ + if (INTEGER_P (type) + && len < SPARC_INTREG_SIZE) + { + struct type *sparc_intreg_type = + TYPE_LENGTH (builtin_type_long) == SPARC_INTREG_SIZE ? + builtin_type_long : builtin_type_long_long; + + val = value_cast (sparc_intreg_type, val); + len = SPARC_INTREG_SIZE; + } + + if (slotno == -1) + { + /* Purely in memory on the stack */ + write_memory (*sp, VALUE_CONTENTS (val), len); + goto advance; + } + + if ((type == TYPE_CODE_FLT + || type == TYPE_CODE_COMPLEX) + && (regno >= FP0_REGNUM && regno < FP_MAX_REGNUM)) + { + /* We copy the value onto the stack always even though + in some cases the v9 ABI allows that to be optimized + away. */ + write_memory (*sp, VALUE_CONTENTS (val), len); + write_register_bytes (REGISTER_BYTE (regno), + VALUE_CONTENTS (val), + len); + + /* If this is one of the first 6 argument slots, we also copy + to the integer register of the same slot to handle varargs + properly. */ + if ((regno - FP0_REGNUM) < SPARC_INT_ARG_MAX * 2) + { + int intreg = O0_REGNUM + (regno - FP0_REGNUM) / 2; + + write_register_bytes (REGISTER_BYTE (intreg), + VALUE_CONTENTS (val), + len); + } + } + else if (type == TYPE_CODE_STRUCT + || type == TYPE_CODE_UNION) + { + /* Pass by reference of large structs is handled entirely in + valops.c via REG_STRUCT_HAS_ADDR. */ + gdb_assert (len <= 16); + + /* On the stack, but partially in registers too. */ + write_memory (*sp, VALUE_CONTENTS (val), len); + if (type == TYPE_CODE_UNION) + { + /* Simplest case. */ + write_register_bytes (REGISTER_BYTE (regno), + VALUE_CONTENTS (val), + len); + } + else + { + /* Complex case :( */ + sp64_push_one_struct_regs (slotno, val, type, len); + } + } + else + { + struct value *copyval = val; + int copylen = len; + + write_memory (*sp, VALUE_CONTENTS (val), len); + write_register_bytes (REGISTER_BYTE (regno), + VALUE_CONTENTS (val), + len); + } + + advance: + orig_cur_word = cur_word; + sp64_arg_advance (&cur_word, val); + *sp += ((cur_word - orig_cur_word) * SPARC_INTREG_SIZE); +} CORE_ADDR sparc64_push_arguments (int nargs, struct value **args, CORE_ADDR sp, int struct_return, CORE_ADDR struct_retaddr) { - int i, j, register_counter = 0; - CORE_ADDR tempsp; + int i, j; + CORE_ADDR tempsp, struct_buf_sp; struct type *sparc_intreg_type = TYPE_LENGTH (builtin_type_long) == SPARC_INTREG_SIZE ? builtin_type_long : builtin_type_long_long; + int arg_words; sp = (sp & ~(((unsigned long) SPARC_INTREG_SIZE) - 1UL)); /* Figure out how much space we'll need. */ - for (i = nargs - 1; i >= 0; i--) - { - int len = TYPE_LENGTH (check_typedef (VALUE_TYPE (args[i]))); - struct value *copyarg = args[i]; - int copylen = len; + sp64_compute_arg_stack_space(&arg_words, nargs, args, struct_return); - if (copylen < SPARC_INTREG_SIZE) - { - copyarg = value_cast (sparc_intreg_type, copyarg); - copylen = SPARC_INTREG_SIZE; - } - sp -= copylen; - } + /* Make room for at least 6 integer arguments for the sake of + varargs. Otherwise a varargs function, once called via + a CALL_DUMMY, can write over other parts of the stack. */ + if (arg_words < 6) + arg_words = 6; - /* Round down. */ - sp = sp & ~7; + sp -= arg_words * SPARC_INTREG_SIZE; tempsp = sp; /* if STRUCT_RETURN, then first argument is the struct return location. */ if (struct_return) - write_register (O0_REGNUM + register_counter++, struct_retaddr); + { + write_register (O0_REGNUM, struct_retaddr); + write_memory (tempsp, (char *) &struct_retaddr, SPARC_INTREG_SIZE); - /* Now write the arguments onto the stack, while writing FP - arguments into the FP registers, and other arguments into the - first six 'O' registers. */ + tempsp += SPARC_INTREG_SIZE; + } + /* now actually push the arguments */ for (i = 0; i < nargs; i++) { - int len = TYPE_LENGTH (check_typedef (VALUE_TYPE (args[i]))); - struct value *copyarg = args[i]; - enum type_code typecode = TYPE_CODE (VALUE_TYPE (args[i])); - int copylen = len; - - if (typecode == TYPE_CODE_INT || - typecode == TYPE_CODE_BOOL || - typecode == TYPE_CODE_CHAR || - typecode == TYPE_CODE_RANGE || - typecode == TYPE_CODE_ENUM) - if (len < SPARC_INTREG_SIZE) - { - /* Small ints will all take up the size of one intreg on - the stack. */ - copyarg = value_cast (sparc_intreg_type, copyarg); - copylen = SPARC_INTREG_SIZE; - } - - write_memory (tempsp, VALUE_CONTENTS (copyarg), copylen); - tempsp += copylen; - - /* Corner case: Structs consisting of a single float member are floats. - * FIXME! I don't know about structs containing multiple floats! - * Structs containing mixed floats and ints are even more weird. - */ - + struct value *val = args[i]; + enum type_code type = TYPE_CODE (VALUE_TYPE (val)); + int len = TYPE_LENGTH (check_typedef (VALUE_TYPE (val))); - - /* Separate float args from all other args. */ - if (typecode == TYPE_CODE_FLT && SPARC_HAS_FPU) - { - if (register_counter < 16) - { - /* This arg gets copied into a FP register. */ - int fpreg; - - switch (len) { - case 4: /* Single-precision (float) */ - fpreg = FP0_REGNUM + 2 * register_counter + 1; - register_counter += 1; - break; - case 8: /* Double-precision (double) */ - fpreg = FP0_REGNUM + 2 * register_counter; - register_counter += 1; - break; - case 16: /* Quad-precision (long double) */ - fpreg = FP0_REGNUM + 2 * register_counter; - register_counter += 2; - break; - default: - internal_error (__FILE__, __LINE__, "bad switch"); - } - write_register_bytes (REGISTER_BYTE (fpreg), - VALUE_CONTENTS (args[i]), - len); - } - } - else /* all other args go into the first six 'o' registers */ - { - for (j = 0; - j < len && register_counter < 6; - j += SPARC_INTREG_SIZE) - { - int oreg = O0_REGNUM + register_counter; - - write_register_gen (oreg, VALUE_CONTENTS (copyarg) + j); - register_counter += 1; - } - } + sp64_push_one_arg (&tempsp, sp, + val, type, len); } + return sp; } @@ -2441,7 +2898,9 @@ sp64_extract_return_value (struct type * int typelen = TYPE_LENGTH (type); int regsize = REGISTER_RAW_SIZE (O0_REGNUM); - if (TYPE_CODE (type) == TYPE_CODE_FLT && SPARC_HAS_FPU) + if ((TYPE_CODE (type) == TYPE_CODE_FLT + || TYPE_CODE (type) == TYPE_CODE_COMPLEX) + && SPARC_HAS_FPU) { memcpy (valbuf, ®buf[REGISTER_BYTE (FP0_REGNUM)], typelen); return;