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]

[RFA] sh-tdep.c: Implement calling convention specific argument passing


Hi,

this is the last patch for SH which introduces the Renesas ABI.  This patch
is necessary to pass arguments correctly to Renesas ABI functions.  There
are a couple of differences between GCC and Renesas ABI in argument passing:

In FPU ABI:

- Doubles are always passed in a even register number pair (r4/r5, r6/r7,
  etc.)  In GCC ABI, if a double is passed after a float, the float goes
  into r4, the double into r6/r7.  If the next argument is a float again,
  GCC uses the next register, r8 in this case.  The r5 register remains
  unused.  Not so in Renesas ABI, which reuses unused registers in this
  case, so the 2nd float would go into r5.

- In little endian mode, the GCC ABI passes multiple floats always in an
  odd register first, so floats are passed in the order r5, r4, r7, r6, ...
  The Renesas ABI always uses registers in natural order as GCC does
  in big endian mode, r4, r5, r6, ...

In no-FPU ABI:

- In Renesas ABI, doubles and long doubles are always passed on the stack.

In either ABI:

- In Renesas ABI, long longs and aggregate types are always passed on
  stack.  Further arguments still can go into registers as they fit.

- In case of varargs calls, the Renesas ABI always passes the last non-
  variable and all variable arguments on the stack.  GCC uses registers
  as available

- When struct return convention is used, the GCC ABI stores the struct
  return address in r2, the Renesas ABI stores the address as last value
  on the stack.

The below patch implements this and adds a bunch of comments to explain
everything.


Corinna


	* sh-tdep.c (sh_next_flt_argreg): Get function type as parameter.
	Take Renesas ABI into account.
	(sh_push_dummy_call_fpu): Ditto.
	(sh_push_dummy_call_nofpu): Ditto.

--- sh-tdep.c.PRE-USE-CC-3	2004-10-20 12:17:24.000000000 +0200
+++ sh-tdep.c	2004-10-20 12:26:13.000000000 +0200
@@ -958,7 +958,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 (int len)
+sh_next_flt_argreg (struct type *func_type, int len)
 {
   int argreg;
 
@@ -977,7 +977,10 @@ 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_effective_calling_convention (func_type) != sh_cc_renesas)
+	    flt_argreg_array[argreg] = 1;
 
 	  ++argreg;
 
@@ -988,7 +991,8 @@ sh_next_flt_argreg (int len)
       /* 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_effective_calling_convention (func_type) != sh_cc_renesas)
     {
       /* In little endian, gcc passes floats like this: f5, f4, f7, f6, ... */
       if (!flt_argreg_array[argreg + 1])
@@ -1066,14 +1070,22 @@ sh_push_dummy_call_fpu (struct gdbarch *
   int len, reg_size = 0;
   int pass_on_stack = 0;
   int treat_as_flt;
+  int last_reg_arg = INT_MAX;
 
+  struct type *func_type = VALUE_TYPE (function);
+
+  if (sh_effective_calling_convention (func_type) == sh_cc_renesas)
+    {
+      /* Check if function is a varargs function.
+         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 (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);
 
@@ -1096,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 (len);
+	flt_argreg = sh_next_flt_argreg (func_type, len);
+      /* In Renesas ABI, long longs and aggregate types are always passed
+         on stack.  */
+      else if (sh_effective_calling_convention (func_type) == sh_cc_renesas
+	       && ((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.  */
@@ -1107,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;
@@ -1150,6 +1170,19 @@ sh_push_dummy_call_fpu (struct gdbarch *
 	}
     }
 
+  if (struct_return)
+    {
+      if (sh_effective_calling_convention (func_type) != 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);
 
@@ -1175,14 +1208,23 @@ sh_push_dummy_call_nofpu (struct gdbarch
   CORE_ADDR regval;
   char *val;
   int len, reg_size;
+  int pass_on_stack;
+  int last_reg_arg = INT_MAX;
+
+  struct type *func_type = VALUE_TYPE (function);
 
+  if (sh_effective_calling_convention (func_type) == sh_cc_renesas)
+    {
+      /* Check if function is a varargs function.
+         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 (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);
 
@@ -1195,9 +1237,21 @@ sh_push_dummy_call_nofpu (struct gdbarch
       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;
+      /* Renesas ABI pushes doubles and long longs entirely on stack.
+         Same goes for aggregate types.  */
+      if (sh_effective_calling_convention (func_type) == sh_cc_renesas
+          && ((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. */
@@ -1220,6 +1274,19 @@ sh_push_dummy_call_nofpu (struct gdbarch
 	}
     }
 
+  if (struct_return)
+    {
+      if (sh_effective_calling_convention (func_type) != 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);
 

-- 
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat, Inc.


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