This is the mail archive of the gdb-patches@sourceware.org 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]

[RFA] Support for multiple calling conventions, patch 3/3: Use calling convention information to handle differnt ABIs on sh target


This is part three of the multiple calling convention patch.

This patch allows to evaluate the per-function calling convention
on the sh target.  sh64 is not affected by this because the sh64
ABI has a somewhat different history.

The GCC ABI is the default when building applications with GCC
and it's the default for GDB as well.  When using the Renesas
sh compiler, the resulting code uses a slightly different ABI,
which is documented in the below code comments.

Additionally, GCC allows to define functions which use the
Renesas ABI by using the __attribute__ ((renesas)) function attribute.
When GCC generates Dwarf2 debug information for these functions, it
adds a DW_AT_calling_convention attribute to the function die
with the value DW_CC_GNU_renesas_sh, defined in elf/dwarf2.h.

The below patch uses the GCC ABI by default, and the Renesas ABI as soon
as the DW_CC_GNU_renesas_sh calling convention has been given in the
function's DW_AT_calling_convention attribute.  For cases in which no
debug information is available, there's a new CLI setting which allows
to enforce using the Renesas ABI.

Ok to apply?


Thanks,
Corinna


        * sh-tdep.c (sh_cc_gcc): New static string.
        (sh_cc_renesas): Ditto.
        (sh_cc_enum): New static string array.
        (sh_active_calling_convention): New static string pointer denoting
        active user chosen ABI.
        (sh_is_renesas_calling_convention): New function to return function
        specific ABI, or user choice if necessary.
        (sh_use_struct_convention): Rename first argument and turn around its
        meaning.  Check for renesas ABI and return accordingly.
        (sh_use_struct_convention_nofpu): New function.
        (sh_next_flt_argreg): Get function type as third parameter.  Check
        for renesas ABI and choose floating registers accordingly.
        (sh_push_dummy_call_fpu): Check for ABI and choose argument slot and
        struct return slot accordingly.
        (sh_push_dummy_call_nofpu): Ditto.
        (sh_return_value_nofpu): Call sh_use_struct_convention_nofpu from here.
        Evaluate ABI and give to sh_use_struct_convention_nofpu.
        (sh_return_value_fpu):  Evaluate ABI and give to
        sh_use_struct_convention.
        (show_sh_command): New function.
        (set_sh_command): Ditto.
        (_initialize_sh_tdep): Initialize `set/show sh calling-convention
        CLI command.


--- sh-tdep.c.ORIG	2008-04-15 13:46:48.000000000 +0200
+++ sh-tdep.c	2008-04-14 18:33:21.000000000 +0200
@@ -51,9 +51,24 @@
 
 /* sh flags */
 #include "elf/sh.h"
+#include "elf/dwarf2.h"
 /* 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) (struct frame_info *);
 
 #define SH_NUM_REGS 67
@@ -73,6 +88,14 @@ struct sh_frame_cache
   CORE_ADDR saved_sp;
 };
 
+static int
+sh_is_renesas_calling_convention (struct type *func_type)
+{
+  return ((func_type
+	   && TYPE_CALLING_CONVENTION (func_type) == DW_CC_GNU_renesas_sh)
+	  || sh_active_calling_convention == sh_cc_renesas);
+}
+
 static const char *
 sh_sh_register_name (struct gdbarch *gdbarch, int reg_nr)
 {
@@ -783,11 +806,16 @@ sh_skip_prologue (struct gdbarch *gdbarc
 */
 
 static int
-sh_use_struct_convention (int gcc_p, struct type *type)
+sh_use_struct_convention (int renesas_abi, struct type *type)
 {
   int len = TYPE_LENGTH (type);
   int nelem = TYPE_NFIELDS (type);
 
+  /* The Renesas ABI returns aggregate types always on stack.  */
+  if (renesas_abi && (TYPE_CODE (type) == TYPE_CODE_STRUCT
+		      || TYPE_CODE (type) == TYPE_CODE_UNION))
+    return 1;
+
   /* Non-power of 2 length types and types bigger than 8 bytes (which don't
      fit in two registers anyway) use struct convention.  */
   if (len != 1 && len != 2 && len != 4 && len != 8)
@@ -813,6 +841,15 @@ sh_use_struct_convention (int gcc_p, str
   return 1;
 }
 
+static int
+sh_use_struct_convention_nofpu (int renesas_abi, struct type *type)
+{
+  /* The Renesas ABI returns long longs/doubles etc. always on stack.  */
+  if (renesas_abi && TYPE_NFIELDS (type) == 0 && TYPE_LENGTH (type) >= 8)
+    return 1;
+  return sh_use_struct_convention (renesas_abi, type);
+}
+
 static CORE_ADDR
 sh_frame_align (struct gdbarch *ignore, CORE_ADDR sp)
 {
@@ -924,7 +961,7 @@ sh_init_flt_argreg (void)
    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 (struct gdbarch *gdbarch, int len)
+sh_next_flt_argreg (struct gdbarch *gdbarch, int len, struct type *func_type)
 {
   int argreg;
 
@@ -943,7 +980,10 @@ sh_next_flt_argreg (struct gdbarch *gdba
       /* 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_is_renesas_calling_convention (func_type))
+	    flt_argreg_array[argreg] = 1;
 
 	  ++argreg;
 
@@ -954,7 +994,8 @@ sh_next_flt_argreg (struct gdbarch *gdba
       /* Also mark the next register as used. */
       flt_argreg_array[argreg + 1] = 1;
     }
-  else if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE)
+  else if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE
+	   && !sh_is_renesas_calling_convention (func_type))
     {
       /* In little endian, gcc passes floats like this: f5, f4, f7, f6, ... */
       if (!flt_argreg_array[argreg + 1])
@@ -1026,20 +1067,25 @@ sh_push_dummy_call_fpu (struct gdbarch *
   int argreg = ARG0_REGNUM;
   int flt_argreg = 0;
   int argnum;
+  struct type *func_type = value_type (function);
   struct type *type;
   CORE_ADDR regval;
   char *val;
   int len, reg_size = 0;
   int pass_on_stack = 0;
   int treat_as_flt;
+  int last_reg_arg = INT_MAX;
+
+  /* The renesas ABI expects all varargs arguments, plus the last
+     non-vararg argument to be on the stack, no matter how many
+     registers have been used so far.  */
+  if (sh_is_renesas_calling_convention (func_type)
+      && (TYPE_FLAGS (func_type) & TYPE_FLAG_VARARGS))
+    last_reg_arg = TYPE_NFIELDS (func_type) - 2;
 
   /* first force sp to a 4-byte alignment */
   sp = sh_frame_align (gdbarch, sp);
 
-  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);
 
@@ -1062,7 +1108,14 @@ sh_push_dummy_call_fpu (struct gdbarch *
       /* Find out the next register to use for a floating point value. */
       treat_as_flt = sh_treat_as_flt_p (type);
       if (treat_as_flt)
-	flt_argreg = sh_next_flt_argreg (gdbarch, len);
+	flt_argreg = sh_next_flt_argreg (gdbarch, len, func_type);
+      /* In Renesas ABI, long longs and aggregate types are always passed
+	 on stack.  */
+      else if (sh_is_renesas_calling_convention (func_type)
+	       && ((TYPE_CODE (type) == TYPE_CODE_INT && len == 8)
+		   || TYPE_CODE (type) == TYPE_CODE_STRUCT
+		   || TYPE_CODE (type) == TYPE_CODE_UNION))
+	pass_on_stack = 1;
       /* In contrast to non-FPU CPUs, arguments are never split between
 	 registers and stack.  If an argument doesn't fit in the remaining
 	 registers it's always pushed entirely on the stack.  */
@@ -1073,7 +1126,8 @@ sh_push_dummy_call_fpu (struct gdbarch *
 	{
 	  if ((treat_as_flt && flt_argreg > FLOAT_ARGLAST_REGNUM)
 	      || (!treat_as_flt && (argreg > ARGLAST_REGNUM
-	                            || pass_on_stack)))
+	                            || pass_on_stack))
+	      || argnum > last_reg_arg)
 	    {
 	      /* The data goes entirely on the stack, 4-byte aligned. */
 	      reg_size = (len + 3) & ~3;
@@ -1116,6 +1170,19 @@ sh_push_dummy_call_fpu (struct gdbarch *
 	}
     }
 
+  if (struct_return)
+    {
+      if (sh_is_renesas_calling_convention (func_type))
+	/* 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);
+      else
+	/* 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);
+    }
+
   /* Store return address. */
   regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr);
 
@@ -1138,18 +1205,24 @@ sh_push_dummy_call_nofpu (struct gdbarch
   int stack_offset = 0;
   int argreg = ARG0_REGNUM;
   int argnum;
+  struct type *func_type = value_type (function);
   struct type *type;
   CORE_ADDR regval;
   char *val;
-  int len, reg_size;
+  int len, reg_size = 0;
+  int pass_on_stack = 0;
+  int last_reg_arg = INT_MAX;
+
+  /* The renesas ABI expects all varargs arguments, plus the last
+     non-vararg argument to be on the stack, no matter how many
+     registers have been used so far.  */
+  if (sh_is_renesas_calling_convention (func_type)
+      && (TYPE_FLAGS (func_type) & TYPE_FLAG_VARARGS))
+    last_reg_arg = TYPE_NFIELDS (func_type) - 2;
 
   /* first force sp to a 4-byte alignment */
   sp = sh_frame_align (gdbarch, sp);
 
-  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);
 
@@ -1162,9 +1235,21 @@ sh_push_dummy_call_nofpu (struct gdbarch
       len = TYPE_LENGTH (type);
       val = sh_justify_value_in_reg (gdbarch, args[argnum], len);
 
+      /* Some decisions have to be made how various types are handled.
+	 This also differs in different ABIs. */
+      pass_on_stack = 0;
+      /* Renesas ABI pushes doubles and long longs entirely on stack.
+	 Same goes for aggregate types.  */
+      if (sh_is_renesas_calling_convention (func_type)
+	  && ((TYPE_CODE (type) == TYPE_CODE_INT && len >= 8)
+	      || (TYPE_CODE (type) == TYPE_CODE_FLT && len >= 8)
+	      || TYPE_CODE (type) == TYPE_CODE_STRUCT
+	      || TYPE_CODE (type) == TYPE_CODE_UNION))
+	pass_on_stack = 1;
       while (len > 0)
 	{
-	  if (argreg > ARGLAST_REGNUM)
+	  if (argreg > ARGLAST_REGNUM || pass_on_stack
+	      || argnum > last_reg_arg)
 	    {
 	      /* The remainder of the data goes entirely on the stack,
 	         4-byte aligned. */
@@ -1187,6 +1272,19 @@ sh_push_dummy_call_nofpu (struct gdbarch
 	}
     }
 
+  if (struct_return)
+    {
+      if (sh_is_renesas_calling_convention (func_type))
+	/* 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);
+      else
+	/* 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);
+    }
+
   /* Store return address. */
   regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr);
 
@@ -1296,7 +1394,8 @@ sh_return_value_nofpu (struct gdbarch *g
 		       struct type *type, struct regcache *regcache,
 		       gdb_byte *readbuf, const gdb_byte *writebuf)
 {
-  if (sh_use_struct_convention (0, type))
+  if (sh_use_struct_convention_nofpu (
+  	sh_is_renesas_calling_convention (func_type), type))
     return RETURN_VALUE_STRUCT_CONVENTION;
   if (writebuf)
     sh_store_return_value_nofpu (type, regcache, writebuf);
@@ -1310,7 +1409,8 @@ sh_return_value_fpu (struct gdbarch *gdb
 		     struct type *type, struct regcache *regcache,
 		     gdb_byte *readbuf, const gdb_byte *writebuf)
 {
-  if (sh_use_struct_convention (0, type))
+  if (sh_use_struct_convention (
+	sh_is_renesas_calling_convention (func_type), type))
     return RETURN_VALUE_STRUCT_CONVENTION;
   if (writebuf)
     sh_store_return_value_fpu (type, regcache, writebuf);
@@ -2879,6 +2979,20 @@ 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
@@ -2889,4 +3003,20 @@ _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_setshow_enum_cmd ("calling-convention", class_vars, sh_cc_enum,
+			&sh_active_calling_convention,
+			_("Set calling convention used when calling target "
+			  "functions from GDB."),
+			_("Show calling convention used when calling target "
+			  "functions from GDB."),
+			_("gcc       - Use GCC calling convention (default).\n"
+			  "renesas   - Enforce Renesas calling convention."),
+			NULL, NULL,
+			&setshcmdlist, &showshcmdlist);
 }


-- 
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat


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