2003-09-12 Andrew Cagney <cagney@redhat.com>
authorAndrew Cagney <cagney@redhat.com>
Fri, 12 Sep 2003 18:55:24 +0000 (18:55 +0000)
committerAndrew Cagney <cagney@redhat.com>
Fri, 12 Sep 2003 18:55:24 +0000 (18:55 +0000)
* ppc-sysv-tdep.c (align_up, align_down): Replace "round2" macro.
(ppc_sysv_abi_push_dummy_call): Rewrite, use a two pass loop.

gdb/ChangeLog
gdb/ppc-sysv-tdep.c

index faca64f..18f03da 100644 (file)
@@ -1,5 +1,10 @@
 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.
+
+2003-09-12  Andrew Cagney  <cagney@redhat.com>
+
        * objfiles.h (struct entry_info): Deprecate "entry_file_lowpc" and
        "entry_file_highpc".  Update comments.
        * defs.h (deprecated_inside_entry_file): Rename
index 2f13e29..777cbbc 100644 (file)
 
 #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,271 @@ ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr,
                              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;
 
-  greg = 3;
-  freg = 1;
-  vreg = 2;
-  argstkspace = 0;
-  structstkspace = 0;
+  /* Go through the argument list twice.
 
-  /* 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 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.
 
-  /* 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);
+     Pass 2: Replay the same computation but this time also write the
+     values out to the target.  */
 
-      if (TYPE_CODE (type) == TYPE_CODE_FLT
-          && ppc_floating_point_unit_p (current_gdbarch))
+  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 (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)
+         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)
            {
-             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++;
+             /* 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)
+         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 */
            {
-             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;
+             /* "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);
+                 if (write_pass)
+                   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;
 }