This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
Re: SH follow up, part 1 (was Re: [RFA] sh-tdep.c: Follow up patch to implement two different ABIs)
Corinna Vinschen writes:
> On Tue, Sep 23, 2003 at 04:44:03PM -0400, Elena Zannoni wrote:
> > I think this patch has too much stuff in it. Can you change it to fix
> > the gcc abi (the fpu stuff), and then add the rest for the renesas
> > variant?
>
> Ok, step 1:
>
Seems ok, are there diffs in the test results before&after? Are these
explanations also in the code? If not, can you please add them?
> - To simplify the sh_push_dummy_call*() functions, I created two helper
> functions which took the part of right-justifying values < 4 byte,
> called sh_justify_value_in_reg(), and to count the number of bytes
> to be allocated on stack for all arguments, called sh_stack_allocsize().
>
> - Two new functions are now used to evaluate the next floating point
> register to use for an argument:
>
> - gcc passes floats in little-endian mode using regsters criss-crossing,
> fr5, fr4, fr7, fr6, fr9, fr8 ... In big-endian mode the order is simple
> fr4, fr5, fr6, ...
>
> - Doubles are passed in even register pairs, fr4/fr5, fr6/fr7, ...
> Example:
>
> void foo(float a, double b, float c);
>
> a is passed in fr4, b then skips the odd-numbered fr5 register, so it's
> passed in fr6/fr7 and c is then passed in the next free register, fr8.
>
> - I renamed the flag `odd_sized_struct' to a more descriptive name,
> `pass_on_stack' since the old name does in no way reflect how the
> decision about passing on stack or in register is actually made. The
> actual decision is as follows:
>
> - On FPU CPUs, pass in registers unless the datatype is bigger than 16 bytes.
>
> - On non-FPU CPUs, no special case exists.
>
> - On all CPUs, everything else is passed in registers until the argument
> registers are filled up, the remaining arguments are passed on stack.
> If an argument doesn't fit entirely in the remaining registers, it's
> split between regs and stack as see fit.
>
> - The code and comment that some data is sometimes passed in registers
> *and* stack simultaneously is dropped. That's not how it works.
>
> > Actually this is part of the original port as contributed by Steve
> > Chamberlain. Is this true of gcc or of the original hitachi abi?
>
> Neither, nor. It must have been some sort of misunderstanding or a bug
> in an ancient gcc. I talked about this with Alexandre Oliva and he has
> no idea when this has ever been the case for gcc at all.
>
> - Also in sh_push_dummy_call*(), the code which adds 4 for every 4 bytes
> managed, is changed from e.g.
>
> len -= register_size (gdbarch, argreg);
>
> to
>
> len -= 4;
>
> The reason is that the original line is not exactly correct. It adds the
> register_size of the *next* register, not the register actually filled
> with data. Since all data and registers in question are 4 byte regs (and
> the target specific code should know that anyway), I've simplified the
> affected code.
>
> > I don't like to have harcoded register sizes, if argreg is wrong, is
> > there a way to get the correct parameter?
>
> All registers are always 4 byte. It's easy enough and an additional comment
> would be sufficient.
>
> If you don't like that and want to use register_size correctly in that case,
> it would have to look like this:
>
Yes please.
When you are done with this rewrite, it would be good to pass the file
through gdb_indent.sh.
elena
> --- sh-tdep.c 2003-09-24 11:29:00.000000000 +0200
> +++ sh-tdep.c.weia 2003-09-24 11:46:28.000000000 +0200
> @@ -1046,7 +1046,7@@ sh_push_dummy_call_fpu (struct gdbarch *
> struct type *type;
> CORE_ADDR regval;
> char *val;
> - int len;
> + int len, reg_size;
> int pass_on_stack;
>
> /* first force sp to a 4-byte alignment */
> @@ -1098,6 +1098,7 @@ sh_push_dummy_call_fpu (struct gdbarch *
> && flt_argreg <= FLOAT_ARGLAST_REGNUM)
> {
> /* Argument goes in a float argument register. */
> + reg_size = register_size (gdbarch, flt_argreg);
> regval = extract_unsigned_integer (val, register_size (gdbarch,
> argreg));
> regcache_cooked_write_unsigned (regcache, flt_argreg++, regval);
> @@ -1105,6 +1106,7 @@ sh_push_dummy_call_fpu (struct gdbarch *
> else if (argreg <= ARGLAST_REGNUM)
> {
> /* there's room in a register */
> + reg_size = register_size (gdbarch, argreg);
> regval = extract_unsigned_integer (val, register_size (gdbarch,
> argreg));
> regcache_cooked_write_unsigned (regcache, argreg++, regval);
> @@ -1112,8 +1114,8 @@ sh_push_dummy_call_fpu (struct gdbarch *
> /* Store the value 4 bytes at a time. This means that things
> larger than 4 bytes may go partly in registers and partly
> on the stack. */
> - len -= 4;
> - val += 4;
> + len -= reg_size;
> + val += reg_size;
> }
> }
>
> Corinna
>
>
> ChangeLog:
> ==========
>
> * sh-tdep.c (sh_justify_value_in_reg): New function.
> (sh_stack_allocsize): Ditto.
> (flt_argreg_array): New array used for floating point argument
> passing.
> (sh_init_flt_argreg): New function.
> (sh_next_flt_argreg): Ditto.
> (sh_push_dummy_call_fpu): Simplify. Rename "odd_sized_struct" to
> "pass_on_stack". Use new helper functions. Accomodate Renesas ABI.
> Fix argument passing strategy.
> (sh_push_dummy_call_nofpu): Ditto.
>
> Index: sh-tdep.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/sh-tdep.c,v
> retrieving revision 1.141
> diff -u -p -r1.141 sh-tdep.c
> --- sh-tdep.c 16 Sep 2003 18:56:35 -0000 1.141
> +++ sh-tdep.c 24 Sep 2003 09:29:10 -0000
> @@ -938,6 +938,98 @@ sh_frame_align (struct gdbarch *ignore,
> not displace any of the other arguments passed in via registers R4
> to R7. */
>
> +/* Helper function to justify value in register according to endianess. */
> +static char *
> +sh_justify_value_in_reg (struct value *val, int len)
> +{
> + static char valbuf[4];
> +
> + memset (valbuf, 0, sizeof (valbuf));
> + if (len < 4)
> + {
> + /* value gets right-justified in the register or stack word */
> + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
> + memcpy (valbuf + (4 - len), (char *) VALUE_CONTENTS (val), len);
> + else
> + memcpy (valbuf, (char *) VALUE_CONTENTS (val), len);
> + return valbuf;
> + }
> + return (char *) VALUE_CONTENTS (val);
> +}
> +
> +/* Helper function to eval number of bytes to allocate on stack. */
> +static CORE_ADDR
> +sh_stack_allocsize (int nargs, struct value **args)
> +{
> + int stack_alloc = 0;
> + while (nargs-- > 0)
> + stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[nargs])) + 3) & ~3);
> + return stack_alloc;
> +}
> +
> +/* Helper functions for getting the float arguments right. Registers usage
> + depends on the ABI and the endianess. The comments should enlighten how
> + it's intended to work. */
> +
> +/* This array stores which of the float arg registers are already in use. */
> +static int flt_argreg_array[FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM + 1];
> +
> +/* This function just resets the above array to "no reg used so far". */
> +static void
> +sh_init_flt_argreg (void)
> +{
> + memset (flt_argreg_array, 0, sizeof flt_argreg_array);
> +}
> +
> +/* This function returns the next register to use for float arg passing.
> + It returns either a valid value between FLOAT_ARG0_REGNUM and
> + FLOAT_ARGLAST_REGNUM if a register is available, otherwise it returns
> + FLOAT_ARGLAST_REGNUM + 1 to indicate that no register is available.
> +
> + Note that register number 0 in flt_argreg_array corresponds with the
> + real float register fr4. In contrast to FLOAT_ARG0_REGNUM (value is
> + 29) the parity of the register number is preserved, which is important
> + for the double register passing test (see the "argreg & 1" test below). */
> +static int
> +sh_next_flt_argreg (int len)
> +{
> + int argreg;
> +
> + /* First search for the next free register. */
> + for (argreg = 0; argreg <= FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM; ++argreg)
> + if (!flt_argreg_array[argreg])
> + break;
> +
> + /* No register left? */
> + if (argreg > FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM)
> + return FLOAT_ARGLAST_REGNUM + 1;
> +
> + if (len == 8)
> + {
> + /* Doubles are always starting in a even register number. */
> + if (argreg & 1)
> + {
> + flt_argreg_array[argreg] = 1;
> +
> + ++argreg;
> +
> + /* No register left? */
> + if (argreg > FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM)
> + return FLOAT_ARGLAST_REGNUM + 1;
> + }
> + /* Also mark the next register as used. */
> + flt_argreg_array[argreg + 1] = 1;
> + }
> + else if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE)
> + {
> + /* In little endian, gcc passes floats like this: f5, f4, f7, f6, ... */
> + if (!flt_argreg_array[argreg + 1])
> + ++argreg;
> + }
> + flt_argreg_array[argreg] = 1;
> + return FLOAT_ARG0_REGNUM + argreg;
> +}
> +
> static CORE_ADDR
> sh_push_dummy_call_fpu (struct gdbarch *gdbarch,
> CORE_ADDR func_addr,
> @@ -947,15 +1039,15 @@ sh_push_dummy_call_fpu (struct gdbarch *
> CORE_ADDR sp, int struct_return,
> CORE_ADDR struct_addr)
> {
> - int stack_offset, stack_alloc;
> - int argreg, flt_argreg;
> + int stack_offset = 0;
> + int argreg = ARG0_REGNUM;
> + int flt_argreg;
> int argnum;
> struct type *type;
> CORE_ADDR regval;
> char *val;
> - char valbuf[4];
> int len;
> - int odd_sized_struct;
> + int pass_on_stack;
>
> /* first force sp to a 4-byte alignment */
> sp = sh_frame_align (gdbarch, sp);
> @@ -967,65 +1059,51 @@ sh_push_dummy_call_fpu (struct gdbarch *
> STRUCT_RETURN_REGNUM,
> struct_addr);
>
> - /* Now make sure there's space on the stack */
> - for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++)
> - stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 3) & ~3);
> - sp -= stack_alloc; /* make room on stack for args */
> + /* make room on stack for args */
> + sp -= sh_stack_allocsize (nargs, args);
> +
> + /* Initialize float argument mechanism. */
> + sh_init_flt_argreg ();
>
> /* Now load as many as possible of the first arguments into
> registers, and push the rest onto the stack. There are 16 bytes
> in four registers available. Loop thru args from first to last. */
> -
> - argreg = ARG0_REGNUM;
> - flt_argreg = FLOAT_ARG0_REGNUM;
> - for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++)
> + for (argnum = 0; argnum < nargs; argnum++)
> {
> type = VALUE_TYPE (args[argnum]);
> len = TYPE_LENGTH (type);
> - memset (valbuf, 0, sizeof (valbuf));
> - if (len < 4)
> - {
> - /* value gets right-justified in the register or stack word */
> - if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
> - memcpy (valbuf + (4 - len),
> - (char *) VALUE_CONTENTS (args[argnum]), len);
> - else
> - memcpy (valbuf, (char *) VALUE_CONTENTS (args[argnum]), len);
> - val = valbuf;
> - }
> - else
> - val = (char *) VALUE_CONTENTS (args[argnum]);
> + val = sh_justify_value_in_reg (args[argnum], len);
> +
> + /* Some decisions have to be made how various types are handled.
> + This also differs in different ABIs. */
> + pass_on_stack = 0;
> + if (len > 16)
> + pass_on_stack = 1; /* Types bigger than 16 bytes are passed on stack. */
> +
> + /* Find out the next register to use for a floating point value. */
> + if (TYPE_CODE (type) == TYPE_CODE_FLT)
> + flt_argreg = sh_next_flt_argreg (len);
>
> - if (len > 4 && (len & 3) != 0)
> - odd_sized_struct = 1; /* Such structs go entirely on stack. */
> - else if (len > 16)
> - odd_sized_struct = 1; /* So do aggregates bigger than 4 words. */
> - else
> - odd_sized_struct = 0;
> while (len > 0)
> {
> if ((TYPE_CODE (type) == TYPE_CODE_FLT
> - && flt_argreg > FLOAT_ARGLAST_REGNUM)
> + && flt_argreg > FLOAT_ARGLAST_REGNUM)
> || argreg > ARGLAST_REGNUM
> - || odd_sized_struct)
> - {
> - /* must go on the stack */
> + || pass_on_stack)
> + {
> write_memory (sp + stack_offset, val, 4);
> stack_offset += 4;
> }
> - /* NOTE WELL!!!!! This is not an "else if" clause!!!
> - That's because some *&^%$ things get passed on the stack
> - AND in the registers! */
> - if (TYPE_CODE (type) == TYPE_CODE_FLT &&
> - flt_argreg > 0 && flt_argreg <= FLOAT_ARGLAST_REGNUM)
> + else if (TYPE_CODE (type) == TYPE_CODE_FLT
> + && flt_argreg <= FLOAT_ARGLAST_REGNUM)
> {
> - /* Argument goes in a single-precision fp reg. */
> + /* Argument goes in a float argument register. */
> regval = extract_unsigned_integer (val, register_size (gdbarch,
> argreg));
> regcache_cooked_write_unsigned (regcache, flt_argreg++, regval);
> }
> else if (argreg <= ARGLAST_REGNUM)
> - {
> + {
> /* there's room in a register */
> regval = extract_unsigned_integer (val, register_size (gdbarch,
> argreg));
> @@ -1034,8 +1112,8 @@ sh_push_dummy_call_fpu (struct gdbarch *
> /* Store the value 4 bytes at a time. This means that things
> larger than 4 bytes may go partly in registers and partly
> on the stack. */
> - len -= register_size (gdbarch, argreg);
> - val += register_size (gdbarch, argreg);
> + len -= 4;
> + val += 4;
> }
> }
>
> @@ -1057,15 +1135,13 @@ sh_push_dummy_call_nofpu (struct gdbarch
> CORE_ADDR sp, int struct_return,
> CORE_ADDR struct_addr)
> {
> - int stack_offset, stack_alloc;
> - int argreg;
> + int stack_offset = 0;
> + int argreg = ARG0_REGNUM;
> int argnum;
> struct type *type;
> CORE_ADDR regval;
> char *val;
> - char valbuf[4];
> int len;
> - int odd_sized_struct;
>
> /* first force sp to a 4-byte alignment */
> sp = sh_frame_align (gdbarch, sp);
> @@ -1077,52 +1153,27 @@ sh_push_dummy_call_nofpu (struct gdbarch
> STRUCT_RETURN_REGNUM,
> struct_addr);
>
> - /* Now make sure there's space on the stack */
> - for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++)
> - stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 3) & ~3);
> - sp -= stack_alloc; /* make room on stack for args */
> + /* make room on stack for args */
> + sp -= sh_stack_allocsize (nargs, args);
>
> /* Now load as many as possible of the first arguments into
> registers, and push the rest onto the stack. There are 16 bytes
> in four registers available. Loop thru args from first to last. */
> -
> - argreg = ARG0_REGNUM;
> - for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++)
> - {
> + for (argnum = 0; argnum < nargs; argnum++)
> + {
> type = VALUE_TYPE (args[argnum]);
> len = TYPE_LENGTH (type);
> - memset (valbuf, 0, sizeof (valbuf));
> - if (len < 4)
> - {
> - /* value gets right-justified in the register or stack word */
> - if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
> - memcpy (valbuf + (4 - len),
> - (char *) VALUE_CONTENTS (args[argnum]), len);
> - else
> - memcpy (valbuf, (char *) VALUE_CONTENTS (args[argnum]), len);
> - val = valbuf;
> - }
> - else
> - val = (char *) VALUE_CONTENTS (args[argnum]);
> + val = sh_justify_value_in_reg (args[argnum], len);
>
> - if (len > 4 && (len & 3) != 0)
> - odd_sized_struct = 1; /* such structs go entirely on stack */
> - else
> - odd_sized_struct = 0;
> while (len > 0)
> {
> - if (argreg > ARGLAST_REGNUM
> - || odd_sized_struct)
> - {
> - /* must go on the stack */
> + if (argreg > ARGLAST_REGNUM)
> + {
> write_memory (sp + stack_offset, val, 4);
> - stack_offset += 4;
> + stack_offset += 4;
> }
> - /* NOTE WELL!!!!! This is not an "else if" clause!!!
> - That's because some *&^%$ things get passed on the stack
> - AND in the registers! */
> - if (argreg <= ARGLAST_REGNUM)
> - {
> + else if (argreg <= ARGLAST_REGNUM)
> + {
> /* there's room in a register */
> regval = extract_unsigned_integer (val, register_size (gdbarch,
> argreg));
> @@ -1131,8 +1182,8 @@ sh_push_dummy_call_nofpu (struct gdbarch
> /* Store the value 4 bytes at a time. This means that things
> larger than 4 bytes may go partly in registers and partly
> on the stack. */
> - len -= register_size (gdbarch, argreg);
> - val += register_size (gdbarch, argreg);
> + len -= 4;
> + val += 4;
> }
> }
>
> --
> Corinna Vinschen
> Cygwin Developer
> Red Hat, Inc.