This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
SH follow up, part 2 (was Re: [RFA] sh-tdep.c: Follow up patch to implement two different ABIs)
On Tue, Sep 23, 2003 at 04:44:03PM -0400, Elena Zannoni wrote:
> Corinna Vinschen writes:
And now part 2.
> > - Add the commands `set calling_convention <foo>' and
> > `show calling_convention' with <foo> being one of "gcc" and "renesas".
> > Default to "gcc".
>
> Should be using hyphen, not underscore. What happens if you have a
Done. I followed Andrew's suggestion to change this to
set sh calling-convention ...
show sh calling-convention
> file compiled with renesas ABI but you set the calling convention to
> the gcc (or vice versa)? How are the abi supported by gcc? Is there a
> -with-abi-renesas to be given when one compiles the tests? I.e. is it
> possible to run the testsuite with the one specific abi?
Currently there's no way to differ between these two ABIs automatically
though that might change at some later point. The user has to change
the ABI on the command line by hand. The compiler option was -mhitachi,
gcc is just changing to -mrenesas to reflect the company name change.
So the changes introduced with this patch are:
- Differ between a fpu and a nofpu version of sh_use_struct_convention.
The nofpu version behaves like the fpu version, except that it uses
struct convention also for all types >= 8 bytes when the Renesas ABI
is active. This especially affects long longs and doubles which are
then passed on the stack.
- The Renesas ABI passes the address in which structs have to be returned
not in r2 as the gcc ABI, but instead this return address is pushed
onto the stack after all arguments have been pushed. This affects
sh_extract_struct_value_address() as well as the sh_push_dummy_call*()
functions.
- sh_next_flt_argreg() now also takes the ABI into account. The differences
are:
- In Renesas ABI, the criss-crossing register usage in little endian
mode for doubles doesn't happen.
- In both ABIs doubles are passed in even register pairs, fr4/fr5,
fr6/fr7, ... The difference is when a register is skipped to pass
the next double. Example:
void foo(float a, double b, float c);
In gcc ABI, 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.
In Renesas ABI, a is passed in fr4, b is passed as in gcc ABI in
fr6/fr7 but c is passed in the lowest unused register, in this example
in fr5.
- In the Renesas ABI for CPUs with FPU, long longs are not passed in
registers but on stack.
- The struct_return code in both sh_push_dummy_call*() functions is moved
to the end of the function since it must be done after the argument
passing for the Rensas ABI.
- `pass_on_stack' takes the Renesas ABI into account:
- On FPU CPUs, Renesas ABI passes long longs always on the stack.
- On non-FPU CPUs, Renesas ABI passes doubles and long doubles always
on the stack.
Corinna
ChangeLog:
==========
* sh-tdep.c (sh_cc_gcc): New static string.
(sh_cc_renesas): Ditto.
(sh_cc_enum): New array pointing to calling convention strings.
(sh_active_calling_convention): New variable pointing to
current active calling convention.
(sh_use_struct_convention_fpu): New function.
(sh_use_struct_convention_nofpu): New function.
(sh_use_struct_convention): Remove. Superseeded by the previous
two functions.
(sh_extract_struct_value_address): Care for Renesas ABI.
(sh_next_flt_argreg): Accomodate Renesas ABI.
(sh_push_dummy_call_fpu): Ditto.
(sh_push_dummy_call_nofpu): Ditto.
(sh_gdbarch_init): Accomodate new sh_use_struct_convention_fpu and
sh_use_struct_convention_nofpu functions.
(show_sh_command): New function.
(set_sh_command): New function.
(_initialize_sh_tdep): Initialize new "set sh calling-convention",
"show sh calling-convention" commands.
--- sh-tdep.c.MIDNEW 2003-09-24 12:01:46.000000000 +0200
+++ sh-tdep.c.NEW 2003-09-24 12:24:10.000000000 +0200
@@ -55,6 +55,21 @@
/* registers numbers shared with the simulator */
#include "gdb/sim-sh.h"
+/* List of "set sh ..." and "show sh ..." commands. */
+static struct cmd_list_element *setshcmdlist = NULL;
+static struct cmd_list_element *showshcmdlist = NULL;
+
+static const char sh_cc_gcc[] = "gcc";
+static const char sh_cc_renesas[] = "renesas";
+static const char *sh_cc_enum[] =
+{
+ sh_cc_gcc,
+ sh_cc_renesas,
+ NULL
+};
+
+static const char *sh_active_calling_convention = sh_cc_gcc;
+
static void (*sh_show_regs) (void);
#define SH_NUM_REGS 59
@@ -569,10 +584,25 @@ sh_skip_prologue (CORE_ADDR start_pc)
/* Should call_function allocate stack space for a struct return? */
static int
-sh_use_struct_convention (int gcc_p, struct type *type)
+sh_use_struct_convention_fpu (int gcc_p, struct type *type)
{
int len = TYPE_LENGTH (type);
int nelem = TYPE_NFIELDS (type);
+
+ return ((len != 1 && len != 2 && len != 4 && len != 8) || nelem != 1) &&
+ (len != 8 || TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) != 4);
+}
+
+static int
+sh_use_struct_convention_nofpu (int gcc_p, struct type *type)
+{
+ int len = TYPE_LENGTH (type);
+ int nelem = TYPE_NFIELDS (type);
+
+ /* The Renesas ABI returns long longs/doubles etc. always on stack. */
+ if (sh_active_calling_convention == sh_cc_renesas && len >= 8)
+ return 1;
+
return ((len != 1 && len != 2 && len != 4 && len != 8) || nelem != 1) &&
(len != 8 || TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) != 4);
}
@@ -584,8 +614,13 @@ static CORE_ADDR
sh_extract_struct_value_address (struct regcache *regcache)
{
ULONGEST addr;
-
- regcache_cooked_read_unsigned (regcache, STRUCT_RETURN_REGNUM, &addr);
+ if (sh_active_calling_convention != sh_cc_renesas)
+ regcache_cooked_read_unsigned (regcache, STRUCT_RETURN_REGNUM, &addr);
+ else
+ {
+ regcache_cooked_read_unsigned (regcache, SP_REGNUM, &addr);
+ addr = read_memory_unsigned_integer (addr, 4);
+ }
return addr;
}
@@ -653,7 +688,7 @@ sh_justify_value_in_reg (struct value *v
{
static char valbuf[4];
- memset (valbuf, 0, sizeof (valbuf));
+ memset (valbuf, 0, sizeof (valbuf));
if (len < 4)
{
/* value gets right-justified in the register or stack word */
@@ -664,7 +699,7 @@ sh_justify_value_in_reg (struct value *v
return valbuf;
}
return (char *) VALUE_CONTENTS (val);
-}
+}
/* Helper function to eval number of bytes to allocate on stack. */
static CORE_ADDR
@@ -718,18 +753,22 @@ sh_next_flt_argreg (int len)
/* Doubles are always starting in a even register number. */
if (argreg & 1)
{
- flt_argreg_array[argreg] = 1;
+ /* In gcc ABI, the skipped register is lost for further argument
+ passing now. Not so in Renesas ABI. */
+ if (sh_active_calling_convention != sh_cc_renesas)
+ flt_argreg_array[argreg] = 1;
++argreg;
- /* No register left? */
+ /* 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)
+ else if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE
+ && sh_active_calling_convention != sh_cc_renesas)
{
/* In little endian, gcc passes floats like this: f5, f4, f7, f6, ... */
if (!flt_argreg_array[argreg + 1])
@@ -761,13 +800,6 @@ sh_push_dummy_call_fpu (struct gdbarch *
/* first force sp to a 4-byte alignment */
sp = sh_frame_align (gdbarch, sp);
- /* The "struct return pointer" pseudo-argument has its own dedicated
- register */
- if (struct_return)
- regcache_cooked_write_unsigned (regcache,
- STRUCT_RETURN_REGNUM,
- struct_addr);
-
/* make room on stack for args */
sp -= sh_stack_allocsize (nargs, args);
@@ -787,7 +819,10 @@ sh_push_dummy_call_fpu (struct gdbarch *
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. */
+ pass_on_stack = 1; /* Types bigger than 16 bytes are passed on stack. */
+ else if (sh_active_calling_convention == sh_cc_renesas
+ && TYPE_CODE (type) == TYPE_CODE_INT && len == 8)
+ pass_on_stack = 1; /* So are long longs in renesas ABI */
/* Find out the next register to use for a floating point value. */
if (TYPE_CODE (type) == TYPE_CODE_FLT)
@@ -796,10 +831,10 @@ sh_push_dummy_call_fpu (struct gdbarch *
while (len > 0)
{
if ((TYPE_CODE (type) == TYPE_CODE_FLT
- && flt_argreg > FLOAT_ARGLAST_REGNUM)
+ && flt_argreg > FLOAT_ARGLAST_REGNUM)
|| argreg > ARGLAST_REGNUM
|| pass_on_stack)
- {
+ {
write_memory (sp + stack_offset, val, 4);
stack_offset += 4;
}
@@ -812,7 +847,7 @@ sh_push_dummy_call_fpu (struct gdbarch *
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));
@@ -826,6 +861,20 @@ sh_push_dummy_call_fpu (struct gdbarch *
}
}
+ if (struct_return)
+ {
+ if (sh_active_calling_convention != sh_cc_renesas)
+ /* Using the gcc ABI, the "struct return pointer" pseudo-argument has
+ its own dedicated register */
+ regcache_cooked_write_unsigned (regcache,
+ STRUCT_RETURN_REGNUM,
+ struct_addr);
+ else
+ /* If the function uses the renesas ABI, subtract another 4 bytes from
+ the stack and store the struct return address there. */
+ write_memory_unsigned_integer (sp -= 4, 4, struct_addr);
+ }
+
/* Store return address. */
regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr);
@@ -851,17 +900,11 @@ sh_push_dummy_call_nofpu (struct gdbarch
CORE_ADDR regval;
char *val;
int len;
+ int pass_on_stack;
/* first force sp to a 4-byte alignment */
sp = sh_frame_align (gdbarch, sp);
- /* The "struct return pointer" pseudo-argument has its own dedicated
- register */
- if (struct_return)
- regcache_cooked_write_unsigned (regcache,
- STRUCT_RETURN_REGNUM,
- struct_addr);
-
/* make room on stack for args */
sp -= sh_stack_allocsize (nargs, args);
@@ -869,20 +912,27 @@ sh_push_dummy_call_nofpu (struct gdbarch
registers, and push the rest onto the stack. There are 16 bytes
in four registers available. Loop thru args from first to last. */
for (argnum = 0; argnum < nargs; argnum++)
- {
+ {
type = VALUE_TYPE (args[argnum]);
len = TYPE_LENGTH (type);
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 (TYPE_CODE (type) == TYPE_CODE_FLT && len > 4
+ && sh_active_calling_convention == sh_cc_renesas)
+ pass_on_stack = 1; /* Renesas ABI pushes doubles entirely on stack. */
+
while (len > 0)
{
- if (argreg > ARGLAST_REGNUM)
- {
+ if (argreg > ARGLAST_REGNUM || pass_on_stack)
+ {
write_memory (sp + stack_offset, val, 4);
- stack_offset += 4;
+ stack_offset += 4;
}
else if (argreg <= ARGLAST_REGNUM)
- {
+ {
/* there's room in a register */
regval = extract_unsigned_integer (val, register_size (gdbarch,
argreg));
@@ -896,6 +946,20 @@ sh_push_dummy_call_nofpu (struct gdbarch
}
}
+ if (struct_return)
+ {
+ if (sh_active_calling_convention != sh_cc_renesas)
+ /* Using the gcc ABI, the "struct return pointer" pseudo-argument has
+ its own dedicated register */
+ regcache_cooked_write_unsigned (regcache,
+ STRUCT_RETURN_REGNUM,
+ struct_addr);
+ else
+ /* If the function uses the renesas ABI, subtract another 4 bytes from
+ the stack and store the struct return address there. */
+ write_memory_unsigned_integer (sp -= 4, 4, struct_addr);
+ }
+
/* Store return address. */
regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr);
@@ -2115,7 +2179,6 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_print_registers_info (gdbarch, sh_print_registers_info);
set_gdbarch_breakpoint_from_pc (gdbarch, sh_breakpoint_from_pc);
- set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention);
set_gdbarch_print_insn (gdbarch, gdb_print_insn_sh);
set_gdbarch_register_sim_regno (gdbarch, legacy_register_sim_regno);
@@ -2134,6 +2197,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_push_dummy_code (gdbarch, sh_push_dummy_code);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_nofpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_nofpu);
set_gdbarch_frame_args_skip (gdbarch, 0);
set_gdbarch_frameless_function_invocation (gdbarch,
@@ -2169,6 +2233,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value);
set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu);
break;
case bfd_mach_sh_dsp:
@@ -2190,6 +2255,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value);
set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu);
break;
case bfd_mach_sh3_dsp:
@@ -2207,6 +2273,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value);
set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu);
break;
default:
@@ -2223,6 +2290,19 @@ sh_gdbarch_init (struct gdbarch_info inf
return gdbarch;
}
+static void
+show_sh_command (char *args, int from_tty)
+{
+ help_list (showshcmdlist, "show sh ", all_commands, gdb_stdout);
+}
+
+static void
+set_sh_command (char *args, int from_tty)
+{
+ printf_unfiltered ("\"set sh\" must be followed by an appropriate subcommand.\n");
+ help_list (setshcmdlist, "set sh ", all_commands, gdb_stdout);
+}
+
extern initialize_file_ftype _initialize_sh_tdep; /* -Wmissing-prototypes */
void
@@ -2233,4 +2313,16 @@ _initialize_sh_tdep (void)
gdbarch_register (bfd_arch_sh, sh_gdbarch_init, NULL);
add_com ("regs", class_vars, sh_show_regs_command, "Print all registers");
+
+ add_prefix_cmd ("sh", no_class, set_sh_command, "SH specific commands.",
+ &setshcmdlist, "set sh ", 0, &setlist);
+ add_prefix_cmd ("sh", no_class, show_sh_command, "SH specific commands.",
+ &showshcmdlist, "show sh ", 0, &showlist);
+
+ add_show_from_set (
+ add_set_enum_cmd ("calling-convention", class_vars, sh_cc_enum,
+ &sh_active_calling_convention,
+ "Set calling convention used when calling target "
+ "functions from GDB.",
+ &setshcmdlist), &showshcmdlist);
}
--
Corinna Vinschen
Cygwin Developer
Red Hat, Inc.