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]

Re: [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.

Oops, try this. It's got the up-to-date changelog and uses a more readable context diff.


Andrew

2003-09-12  Andrew Cagney  <cagney@redhat.com>
 
 	* ppc-sysv-tdep.c (align_up, align_down): 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 -c -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:57:41 -0000
***************
*** 29,39 ****
  
  #include "ppc-tdep.h"
  
! /* round2 rounds x up to the nearest multiple of s assuming that s is a
!    power of 2 */
  
! #undef round2
! #define round2(x,s) ((((long) (x) - 1) & ~(long)((s)-1)) + (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
--- 29,48 ----
  
  #include "ppc-tdep.h"
  
! /* Ensure that X is aligned to an S byte boundary (assuming that S is
!    a power of 2) rounding up/down where necessary.  */
  
! 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,349 ****
  			      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);
  
!   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++;
!     }
  
!   /* 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++)
      {
!       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))
  	{
! 	  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;
! 	    }
  	}
!       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 (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++;
  	      else
  		{
! 		  /* Vector arguments must be aligned to 16 bytes on
!                      the stack. */
! 		  argstkspace += round2 (argstkspace, 16);
! 		  argstkspace += 16;
  		}
  	    }
!           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 == 16
! 	      && TYPE_CODE (type) == TYPE_CODE_ARRAY
! 	      && TYPE_VECTOR (type))
  	    {
! 	      char *v_val_buf = alloca (16);
! 	      memset (v_val_buf, 0, 16);
! 	      memcpy (v_val_buf, val, len);
  	      if (vreg <= 13)
  		{
! 		  regcache_cooked_write (current_regcache,
! 					 tdep->ppc_vr0_regnum + vreg,
! 					 v_val_buf);
  		  vreg++;
  		}
  	      else
  		{
! 		  write_memory (sp + argoffset, v_val_buf, 16);
  		  argoffset += 16;
  		}
  	    }
            else if (len == 8 
  		   && TYPE_CODE (type) == TYPE_CODE_ARRAY
! 		   && TYPE_VECTOR (type))
              {
!               char *v_val_buf = alloca (8);
!               memset (v_val_buf, 0, 8);
!               memcpy (v_val_buf, val, len);
                if (greg <= 10)
                  {
! 		  regcache_cooked_write (current_regcache,
! 					 tdep->ppc_ev0_regnum + greg,
! 					 v_val_buf);
                    greg++;
                  }
                else
                  {
!                   write_memory (sp + argoffset, v_val_buf, 8);
                    argoffset += 8;
                  }
              }
!         }
      }
  
    /* Point the inferior function call's return address at the dummy's
       breakpoint.  */
!   regcache_raw_write_signed (regcache, tdep->ppc_lr_regnum, bp_addr);
  
-   target_store_registers (-1);
    return sp;
  }
  
--- 61,330 ----
  			      int nargs, struct value **args, CORE_ADDR sp,
  			      int struct_return, CORE_ADDR struct_addr)
  {
    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.
  
!      Pass 2: Replay the same computation but this time also write the
!      values out to the target.  */
  
!   for (write_pass = 0; write_pass < 2; write_pass++)
      {
!       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 (write_pass)
! 	    regcache_cooked_write_signed (regcache,
! 					  tdep->ppc_gp0_regnum + greg,
! 					  struct_addr);
! 	  greg++;
  	}
! 
!       for (argno = 0; argno < nargs; argno++)
  	{
! 	  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
  		{
! 		  /* 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_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)
! 		   && tdep->ppc_vr0_regnum >= 0)
  	    {
! 	      /* Vector parameter passed in an Altivec register, or
!                  when that runs out, 16 byte aligned stack location.  */
  	      if (vreg <= 13)
  		{
! 		  if (write_pass)
! 		    regcache_cooked_write (current_regcache,
! 					   tdep->ppc_vr0_regnum + vreg,
! 					   val);
  		  vreg++;
  		}
  	      else
  		{
! 		  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)
! 		   && tdep->ppc_ev0_regnum >= 0)
              {
! 	      /* 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)
                  {
! 		  if (write_pass)
! 		    regcache_cooked_write (current_regcache,
! 					   tdep->ppc_ev0_regnum + greg,
! 					   val);
                    greg++;
                  }
                else
                  {
! 		  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_cooked_write_signed (regcache, tdep->ppc_lr_regnum, bp_addr);
  
    return sp;
  }
  

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