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:ppc] Cleanup SVR4 push dummy call


Hello,

This patch rewrites ppc_svr4_push_dummy_call so that it implements the two-pass push-arguments algorithm using a "for" loop.

The simplification identified and fixed the bugs:

- Altivec registers on the stack weren't being 16 byte aligned
- Floats weren't being converted to doubles before being stored in register/memory ("gdb.base/callfuncs.exp: p t_float_values2" now passes).


Ok?

Andrew

PS: I intend moving align_{up,down} to utils.c but that is separate.
2003-09-12  Andrew Cagney  <cagney@redhat.com>

	* ppc-sysv-tdep.c (round_up): Replace "round2" macro.
	(ppc_sysv_abi_push_dummy_call): Rewrite, use a two pass loop.

Index: ppc-sysv-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-sysv-tdep.c,v
retrieving revision 1.9
diff -u -r1.9 ppc-sysv-tdep.c
--- ppc-sysv-tdep.c	11 Sep 2003 19:27:25 -0000	1.9
+++ ppc-sysv-tdep.c	12 Sep 2003 14:28:44 -0000
@@ -29,11 +29,20 @@
 
 #include "ppc-tdep.h"
 
-/* round2 rounds x up to the nearest multiple of s assuming that s is a
-   power of 2 */
+/* Ensure that X is aligned to an S byte boundary (assuming that S is
+   a power of 2) rounding up/down where necessary.  */
 
-#undef round2
-#define round2(x,s) ((((long) (x) - 1) & ~(long)((s)-1)) + (s))
+static ULONGEST
+align_up (ULONGEST x, int s)
+{
+  return (x + s - 1) & -s;
+}
+
+static ULONGEST
+align_down (ULONGEST x, int s)
+{
+  return (x & -s);
+}
 
 /* Pass the arguments in either registers, or in the stack. Using the
    ppc sysv ABI, the first eight words of the argument list (that might
@@ -52,298 +61,270 @@
 			      int nargs, struct value **args, CORE_ADDR sp,
 			      int struct_return, CORE_ADDR struct_addr)
 {
-  int argno;
-  /* Next available general register for non-float, non-vector arguments. */
-  int greg;
-  /* Next available floating point register for float arguments. */
-  int freg;
-  /* Next available vector register for vector arguments. */
-  int vreg;
-  int argstkspace;
-  int structstkspace;
-  int argoffset;
-  int structoffset;
-  struct type *type;
-  int len;
-  char old_sp_buf[4];
-  CORE_ADDR saved_sp;
   struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+  const CORE_ADDR saved_sp = read_sp ();
+  int argspace = 0;		/* 0 is an initial wrong guess.  */
+  int write_pass;
+
+  /* Go through the argument list twice.
+
+     Pass 1: Figure out how much new stack space is required for
+     arguments and pushed values.  Unlike the PowerOpen ABI, the SysV
+     ABI doesn't reserve any extra space for parameters which are put
+     in registers, but does always push structures and then pass their
+     address.
 
-  greg = 3;
-  freg = 1;
-  vreg = 2;
-  argstkspace = 0;
-  structstkspace = 0;
-
-  /* If the function is returning a `struct', then the first word
-     (which will be passed in r3) is used for struct return address.
-     In that case we should advance one word and start from r4
-     register to copy parameters.  */
-  if (struct_return)
-    {
-      regcache_raw_write_signed (regcache, tdep->ppc_gp0_regnum + greg,
-				 struct_addr);
-      greg++;
-    }
+     Pass 2: Replay the same computation but this time also write the
+     values out to the target.  */
 
-  /* Figure out how much new stack space is required for arguments
-     which don't fit in registers.  Unlike the PowerOpen ABI, the
-     SysV ABI doesn't reserve any extra space for parameters which
-     are put in registers. */
-  for (argno = 0; argno < nargs; argno++)
+  for (write_pass = 0; write_pass < 2; write_pass++)
     {
-      struct value *arg = args[argno];
-      type = check_typedef (VALUE_TYPE (arg));
-      len = TYPE_LENGTH (type);
-
-      if (TYPE_CODE (type) == TYPE_CODE_FLT
-          && ppc_floating_point_unit_p (current_gdbarch))
+      int argno;
+      /* Next available floating point register for float and double
+         arguments.  */
+      int freg = 1;
+      /* Next available general register for non-float, non-vector
+         arguments.  */
+      int greg = 3;
+      /* Next available vector register for vector arguments.  */
+      int vreg = 2;
+      /* Arguments start above the "LR save word" and "Back chain".  */
+      int argoffset = 2 * tdep->wordsize;
+      /* Structures start after the arguments.  */
+      int structoffset = argoffset + argspace;
+
+      /* If the function is returning a `struct', then the first word
+	 (which will be passed in r3) is used for struct return
+	 address.  In that case we should advance one word and start
+	 from r4 register to copy parameters.  */
+      if (struct_return)
 	{
-	  if (freg <= 8)
-	    freg++;
-	  else
-	    {
-	      /* SysV ABI converts floats to doubles when placed in
-	         memory and requires 8 byte alignment */
-	      if (argstkspace & 0x4)
-		argstkspace += 4;
-	      argstkspace += 8;
-	    }
+	  if (write_pass)
+	    regcache_cooked_write_signed (regcache,
+					  tdep->ppc_gp0_regnum + greg,
+					  struct_addr);
+	  greg++;
 	}
-      else if (len == 8 
-               && (TYPE_CODE (type) == TYPE_CODE_INT /* long long */
-                   || (!ppc_floating_point_unit_p (current_gdbarch)
-                       && TYPE_CODE (type) == TYPE_CODE_FLT))) /* double */
+
+      for (argno = 0; argno < nargs; argno++)
 	{
-	  if (greg > 9)
-	    {
-	      greg = 11;
-	      if (argstkspace & 0x4)
-		argstkspace += 4;
-	      argstkspace += 8;
-	    }
-	  else
-	    {
-	      if ((greg & 1) == 0)
-		greg++;
-	      greg += 2;
-	    }
-	}
-      else if (!TYPE_VECTOR (type))
-        {
-	  if (len > 4
-	      || TYPE_CODE (type) == TYPE_CODE_STRUCT
-	      || TYPE_CODE (type) == TYPE_CODE_UNION)
-	    {
-	      /* Rounding to the nearest multiple of 8 may not be necessary,
-		 but it is safe.  Particularly since we don't know the
-		 field types of the structure */
-	      structstkspace += round2 (len, 8);
-	    }
-	  if (greg <= 10)
-	    greg++;
-	  else
-	    argstkspace += 4;
-    	}
-      else
-        {
-          if (len == 16
-	      && TYPE_CODE (type) == TYPE_CODE_ARRAY
-	      && TYPE_VECTOR (type))
-	    {
-	      if (vreg <= 13)
-		vreg++;
+	  struct value *arg = args[argno];
+	  struct type *type = check_typedef (VALUE_TYPE (arg));
+	  int len = TYPE_LENGTH (type);
+	  char *val = VALUE_CONTENTS (arg);
+
+	  if (TYPE_CODE (type) == TYPE_CODE_FLT
+	      && ppc_floating_point_unit_p (current_gdbarch)
+	      && len <= 8)
+	    {
+	      /* Floating point value converted to "double" then
+                 passed in an FP register, when the registers run out,
+                 8 byte aligned stack is used.  */
+	      if (freg <= 8)
+		{
+		  if (write_pass)
+		    {
+		      /* Always store the floating point value using
+                         the register's floating-point format.  */
+		      char regval[MAX_REGISTER_SIZE];
+		      struct type *regtype
+			= register_type (gdbarch, FP0_REGNUM + freg);
+		      convert_typed_floating (val, type, regval, regtype);
+		      regcache_cooked_write (regcache, FP0_REGNUM + freg,
+					     regval);
+		    }
+		  freg++;
+		}
 	      else
 		{
-		  /* Vector arguments must be aligned to 16 bytes on
-                     the stack. */
-		  argstkspace += round2 (argstkspace, 16);
-		  argstkspace += 16;
+		  /* SysV ABI converts floats to doubles before
+                     writing them to an 8 byte aligned stack location.  */
+		  argoffset = align_up (argoffset, 8);
+		  if (write_pass)
+		    {
+		      char memval[8];
+		      struct type *memtype;
+		      switch (TARGET_BYTE_ORDER)
+			{
+			case BFD_ENDIAN_BIG:
+			  memtype = builtin_type_ieee_double_big;
+			  break;
+			case BFD_ENDIAN_LITTLE:
+			  memtype = builtin_type_ieee_double_little;
+			  break;
+			default:
+			  internal_error (__FILE__, __LINE__, "bad switch");
+			}
+		      convert_typed_floating (val, type, memval, memtype);
+		      write_memory (sp + argoffset, val, len);
+		    }
+		  argoffset += 8;
 		}
 	    }
-          else if (len == 8 
-                   && TYPE_CODE (type) == TYPE_CODE_ARRAY
-                   && TYPE_VECTOR (type))
-            {
-              if (greg <= 10)
-                greg++;
-              else
-                {
-                  /* Vector arguments must be aligned to 8 bytes on
-                     the stack. */
-                  argstkspace += round2 (argstkspace, 8);
-                  argstkspace += 8;
-                }
-            }
-	}
-    }
-
-  /* Get current SP location */
-  saved_sp = read_sp ();
-
-  sp -= argstkspace + structstkspace;
-
-  /* Allocate space for backchain and callee's saved lr */
-  sp -= 8;
-
-  /* Make sure that we maintain 16 byte alignment */
-  sp &= ~0x0f;
-
-  /* Update %sp before proceeding any further.   */
-  regcache_raw_write_signed (regcache, SP_REGNUM, sp);
-
-  /* write the backchain */
-  store_unsigned_integer (old_sp_buf, 4, saved_sp);
-  write_memory (sp, old_sp_buf, 4);
-
-  argoffset = 8;
-  structoffset = argoffset + argstkspace;
-  freg = 1;
-  greg = 3;
-  vreg = 2;
-
-  /* Fill in r3 with the return structure, if any */
-  if (struct_return)
-    {
-      write_register (tdep->ppc_gp0_regnum + greg, struct_addr);
-      greg++;
-    }
-
-  /* Now fill in the registers and stack... */
-  for (argno = 0; argno < nargs; argno++)
-    {
-      struct value *arg = args[argno];
-      char *val = VALUE_CONTENTS (arg);
-      type = check_typedef (VALUE_TYPE (arg));
-      len = TYPE_LENGTH (type);
-
-      if (TYPE_CODE (type) == TYPE_CODE_FLT
-          && ppc_floating_point_unit_p (current_gdbarch))
-	{
-	  if (freg <= 8)
-	    {
-	      ULONGEST regval;
-	      if (len > 8)
-		printf_unfiltered (
-				   "Fatal Error: a floating point parameter #%d with a size > 8 is found!\n", argno);
-              regval = extract_unsigned_integer (val, len);
-              write_register (FP0_REGNUM + freg, regval);
-	      freg++;
-	    }
-	  else
-	    {
-	      /* SysV ABI converts floats to doubles when placed in
-	         memory and requires 8 byte alignment */
-	      /* FIXME: Convert floats to doubles */
-	      if (argoffset & 0x4)
-		argoffset += 4;
-	      write_memory (sp + argoffset, val, len);
-	      argoffset += 8;
-	    }
-	}
-      else if (len == 8 
-               && (TYPE_CODE (type) == TYPE_CODE_INT /* long long */
-                   || (!ppc_floating_point_unit_p (current_gdbarch)
-                        && TYPE_CODE (type) == TYPE_CODE_FLT))) /* double */
-	{
-	  if (greg > 9)
-	    {
-	      greg = 11;
-	      if (argoffset & 0x4)
-		argoffset += 4;
-	      write_memory (sp + argoffset, val, len);
-	      argoffset += 8;
-	    }
-	  else
-	    {
-	      ULONGEST regval;
-	      if ((greg & 1) == 0)
-		greg++;
-              regval = extract_unsigned_integer (val, 4);
-              write_register (tdep->ppc_gp0_regnum + greg, regval);
-              regval = extract_unsigned_integer (val + 4, 4);
-              write_register (tdep->ppc_gp0_regnum + greg + 1, regval);
-	      greg += 2;
-	    }
-	}
-      else if (!TYPE_VECTOR (type))
-	{
-	  char val_buf[4];
-	  if (len > 4
-	      || TYPE_CODE (type) == TYPE_CODE_STRUCT
-	      || TYPE_CODE (type) == TYPE_CODE_UNION)
-	    {
-	      write_memory (sp + structoffset, val, len);
-	      store_unsigned_integer (val_buf, 4, sp + structoffset);
-	      structoffset += round2 (len, 8);
-	    }
-	  else
-	    {
-	      memset (val_buf, 0, 4);
-	      memcpy (val_buf, val, len);
-	    }
-	  if (greg <= 10)
-	    {
-              ULONGEST regval = extract_unsigned_integer (val_buf, 4);
-              write_register (tdep->ppc_gp0_regnum + greg, regval);
-	      greg++;
-	    }
-	  else
-	    {
-	      write_memory (sp + argoffset, val_buf, 4);
-	      argoffset += 4;
+	  else if (len == 8 
+		   && (TYPE_CODE (type) == TYPE_CODE_INT /* long long */
+		       || (!ppc_floating_point_unit_p (current_gdbarch)
+			   && TYPE_CODE (type) == TYPE_CODE_FLT))) /* double */
+	    {
+	      /* "long long" or "double" passed in an odd/even
+                 register pair with the low addressed word in the odd
+                 register and the high addressed word in the even
+                 register, or when the registers run out an 8 byte
+                 aligned stack location.  */
+	      if (greg > 9)
+		{
+		  /* Just in case GREG was 10.  */
+		  greg = 11;
+		  argoffset = align_up (argoffset, 8);
+		  write_memory (sp + argoffset, val, len);
+		  argoffset += 8;
+		}
+	      else if (tdep->wordsize == 8)
+		{
+		  if (write_pass)
+		    regcache_cooked_write (regcache,
+					   tdep->ppc_gp0_regnum + greg,
+					   val);
+		  greg += 1;
+		}
+	      else
+		{
+		  /* Must start on an odd register - r3/r4 etc.  */
+		  if ((greg & 1) == 0)
+		    greg++;
+		  if (write_pass)
+		    {
+		      regcache_cooked_write (regcache,
+					     tdep->ppc_gp0_regnum + greg + 0,
+					     val + 0);
+		      regcache_cooked_write (regcache,
+					     tdep->ppc_gp0_regnum + greg + 1,
+					     val + 4);
+		    }
+		  greg += 2;
+		}
 	    }
-	}
-      else
-	{
-	  if (len == 16
-	      && TYPE_CODE (type) == TYPE_CODE_ARRAY
-	      && TYPE_VECTOR (type))
+	  else if (len == 16
+		   && TYPE_CODE (type) == TYPE_CODE_ARRAY
+		   && TYPE_VECTOR (type)
+		   && tdep->ppc_vr0_regnum >= 0)
 	    {
-	      char *v_val_buf = alloca (16);
-	      memset (v_val_buf, 0, 16);
-	      memcpy (v_val_buf, val, len);
+	      /* Vector parameter passed in an Altivec register, or
+                 when that runs out, 16 byte aligned stack location.  */
 	      if (vreg <= 13)
 		{
-		  regcache_cooked_write (current_regcache,
-					 tdep->ppc_vr0_regnum + vreg,
-					 v_val_buf);
+		  if (write_pass)
+		    regcache_cooked_write (current_regcache,
+					   tdep->ppc_vr0_regnum + vreg,
+					   val);
 		  vreg++;
 		}
 	      else
 		{
-		  write_memory (sp + argoffset, v_val_buf, 16);
+		  argoffset = align_up (argoffset, 16);
+		  if (write_pass)
+		    write_memory (sp + argoffset, val, 16);
 		  argoffset += 16;
 		}
 	    }
           else if (len == 8 
 		   && TYPE_CODE (type) == TYPE_CODE_ARRAY
-		   && TYPE_VECTOR (type))
+		   && TYPE_VECTOR (type)
+		   && tdep->ppc_ev0_regnum >= 0)
             {
-              char *v_val_buf = alloca (8);
-              memset (v_val_buf, 0, 8);
-              memcpy (v_val_buf, val, len);
+	      /* Vector parameter passed in an e500 register, or when
+                 that runs out, 8 byte aligned stack location.  Note
+                 that since e500 vector and general purpose registers
+                 both map onto the same underlying register set, a
+                 "greg" and not a "vreg" is consumed here.  A cooked
+                 write stores the value in the correct locations
+                 within the raw register cache.  */
               if (greg <= 10)
                 {
-		  regcache_cooked_write (current_regcache,
-					 tdep->ppc_ev0_regnum + greg,
-					 v_val_buf);
+		  if (write_pass)
+		    regcache_cooked_write (current_regcache,
+					   tdep->ppc_ev0_regnum + greg,
+					   val);
                   greg++;
                 }
               else
                 {
-                  write_memory (sp + argoffset, v_val_buf, 8);
+		  argoffset = align_up (argoffset, 8);
+		  if (write_pass)
+		    write_memory (sp + argoffset, val, 8);
                   argoffset += 8;
                 }
             }
-        }
+	  else
+	    {
+	      /* Reduce the parameter down to something that fits in a
+                 "word".  */
+	      char word[MAX_REGISTER_SIZE];
+	      memset (word, 0, MAX_REGISTER_SIZE);
+	      if (len > tdep->wordsize
+		  || TYPE_CODE (type) == TYPE_CODE_STRUCT
+		  || TYPE_CODE (type) == TYPE_CODE_UNION)
+		{
+		  /* Structs and large values are put on an 8 byte
+                     aligned stack ... */
+		  structoffset = align_up (structoffset, 8);
+		  if (write_pass)
+		    write_memory (sp + structoffset, val, len);
+		  /* ... and then a "word" pointing to that address is
+                     passed as the parameter.  */
+		  store_unsigned_integer (word, tdep->wordsize,
+					  sp + structoffset);
+		  structoffset += len;
+		}
+	      else if (TYPE_CODE (type) == TYPE_CODE_INT)
+		/* Sign or zero extend the "int" into a "word".  */
+		store_unsigned_integer (word, tdep->wordsize,
+					unpack_long (type, val));
+	      else
+		/* Always goes in the low address.  */
+		memcpy (word, val, len);
+	      /* Store that "word" in a register, or on the stack.
+                 The words have "4" byte alignment.  */
+	      if (greg <= 10)
+		{
+		  if (write_pass)
+		    regcache_cooked_write (regcache,
+					   tdep->ppc_gp0_regnum + greg,
+					   word);
+		  greg++;
+		}
+	      else
+		{
+		  argoffset = align_up (argoffset, tdep->wordsize);
+		  if (write_pass)
+		    write_memory (sp + argoffset, word, tdep->wordsize);
+		  argoffset += tdep->wordsize;
+		}
+	    }
+	}
+
+      /* Compute the actual stack space requirements.  */
+      if (!write_pass)
+	{
+	  /* Remember the amount of space needed by the arguments.  */
+	  argspace = argoffset;
+	  /* Allocate space for both the arguments and the structures.  */
+	  sp -= (argoffset + structoffset);
+	  /* Ensure that the stack is still 16 byte aligned.  */
+	  sp = align_down (sp, 16);
+	}
     }
 
+  /* Update %sp.   */
+  regcache_cooked_write_signed (regcache, SP_REGNUM, sp);
+
+  /* Write the backchain (it occupies WORDSIZED bytes).  */
+  write_memory_signed_integer (sp, tdep->wordsize, saved_sp);
+
   /* Point the inferior function call's return address at the dummy's
      breakpoint.  */
-  regcache_raw_write_signed (regcache, tdep->ppc_lr_regnum, bp_addr);
+  regcache_cooked_write_signed (regcache, tdep->ppc_lr_regnum, bp_addr);
 
-  target_store_registers (-1);
   return sp;
 }
 

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