This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]