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] Clone mips_o32o64_push_arguments


Well, I had to fix an ABI-specific bug in mips_push_arguments, 
so I went ahead and cloned it.  Gimme feedback on this function,
and meanwhile I'll go ahead and simplify the remaining push_arguments
for EABI (and submit it separately).
2002-08-13  Michael Snyder  <msnyder@redhat.com>

	* mips-tdep.c (mips_o32o64_push_arguments):  New function, 
	cloned from mips_push_arguments, tuned for o32/o64 ABI.
	(mips_gdbarch_init): Set gdbarch_push_arguments to new func.

Index: mips-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mips-tdep.c,v
retrieving revision 1.99
diff -c -3 -p -r1.99 mips-tdep.c
*** mips-tdep.c	10 Aug 2002 01:09:10 -0000	1.99
--- mips-tdep.c	14 Aug 2002 00:20:52 -0000
*************** mips_push_arguments (int nargs,
*** 2591,2597 ****
  					argreg, phex (regval, 4));
  		  write_register (argreg++, regval);
  		}
- 
  	    }
  	  else
  	    {
--- 2591,2596 ----
*************** mips_push_arguments (int nargs,
*** 2644,2650 ****
               register are only written to memory. */
  	  while (len > 0)
  	    {
! 	      /* Rememer if the argument was written to the stack. */
  	      int stack_used_p = 0;
  	      int partial_len = len < MIPS_SAVED_REGSIZE ? len : MIPS_SAVED_REGSIZE;
  
--- 2643,2649 ----
               register are only written to memory. */
  	  while (len > 0)
  	    {
! 	      /* Remember if the argument was written to the stack.  */
  	      int stack_used_p = 0;
  	      int partial_len = len < MIPS_SAVED_REGSIZE ? len : MIPS_SAVED_REGSIZE;
  
*************** mips_push_arguments (int nargs,
*** 2783,2788 ****
--- 2782,2789 ----
    return sp;
  }
  
+ /* N32/N64 version of push_arguments.  */
+ 
  CORE_ADDR
  mips_n32n64_push_arguments (int nargs,
  			    struct value **args,
*************** mips_n32n64_push_arguments (int nargs,
*** 3007,3012 ****
--- 3008,3313 ----
    return sp;
  }
  
+ /* O32/O64 version of push_arguments.  */
+ 
+ CORE_ADDR
+ mips_o32o64_push_arguments (int nargs,
+ 			    struct value **args,
+ 			    CORE_ADDR sp,
+ 			    int struct_return,
+ 			    CORE_ADDR struct_addr)
+ {
+   int argreg;
+   int float_argreg;
+   int argnum;
+   int len = 0;
+   int stack_offset = 0;
+   struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ 
+   /* First ensure that the stack and structure return address (if any)
+      are properly aligned.  The stack has to be at least 64-bit
+      aligned even on 32-bit machines, because doubles must be 64-bit
+      aligned.  For n32 and n64, stack frames need to be 128-bit
+      aligned, so we round to this widest known alignment.  */
+ 
+   sp = ROUND_DOWN (sp, 16);
+   struct_addr = ROUND_DOWN (struct_addr, 16);
+ 
+   /* Now make space on the stack for the args.  */
+   for (argnum = 0; argnum < nargs; argnum++)
+     len += ROUND_UP (TYPE_LENGTH (VALUE_TYPE (args[argnum])), 
+ 		     MIPS_STACK_ARGSIZE);
+   sp -= ROUND_UP (len, 16);
+ 
+   if (mips_debug)
+     fprintf_unfiltered (gdb_stdlog, 
+ 			"mips_o32o64_push_arguments: sp=0x%s allocated %d\n",
+ 			paddr_nz (sp), ROUND_UP (len, 16));
+ 
+   /* Initialize the integer and float register pointers.  */
+   argreg = A0_REGNUM;
+   float_argreg = FPA0_REGNUM;
+ 
+   /* the struct_return pointer occupies the first parameter-passing reg */
+   if (struct_return)
+     {
+       if (mips_debug)
+ 	fprintf_unfiltered (gdb_stdlog,
+ 			    "mips_o32o64_push_arguments: struct_return reg=%d 0x%s\n",
+ 			    argreg, paddr_nz (struct_addr));
+       write_register (argreg++, struct_addr);
+       stack_offset += MIPS_STACK_ARGSIZE;
+     }
+ 
+   /* Now load as many as possible of the first arguments into
+      registers, and push the rest onto the stack.  Loop thru args
+      from first to last.  */
+   for (argnum = 0; argnum < nargs; argnum++)
+     {
+       char *val;
+       char *valbuf = alloca (MAX_REGISTER_RAW_SIZE);
+       struct value *arg = args[argnum];
+       struct type *arg_type = check_typedef (VALUE_TYPE (arg));
+       int len = TYPE_LENGTH (arg_type);
+       enum type_code typecode = TYPE_CODE (arg_type);
+ 
+       if (mips_debug)
+ 	fprintf_unfiltered (gdb_stdlog,
+ 			    "mips_o32o64_push_arguments: %d len=%d type=%d",
+ 			    argnum + 1, len, (int) typecode);
+ 
+       val = (char *) VALUE_CONTENTS (arg);
+ 
+       /* 32-bit ABIs always start floating point arguments in an
+          even-numbered floating point register.  Round the FP register
+          up before the check to see if there are any FP registers
+          left.  O32/O64 targets also pass the FP in the integer
+          registers so also round up normal registers.  */
+       if (!FP_REGISTER_DOUBLE
+ 	  && fp_register_arg_p (typecode, arg_type))
+ 	{
+ 	  if ((float_argreg & 1))
+ 	    float_argreg++;
+ 	}
+ 
+       /* Floating point arguments passed in registers have to be
+          treated specially.  On 32-bit architectures, doubles
+          are passed in register pairs; the even register gets
+          the low word, and the odd register gets the high word.
+          On O32/O64, the first two floating point arguments are
+          also copied to general registers, because MIPS16 functions
+          don't use float registers for arguments.  This duplication of
+          arguments in general registers can't hurt non-MIPS16 functions
+          because those registers are normally skipped.  */
+ 
+       if (fp_register_arg_p (typecode, arg_type)
+ 	  && float_argreg <= MIPS_LAST_FP_ARG_REGNUM)
+ 	{
+ 	  if (!FP_REGISTER_DOUBLE && len == 8)
+ 	    {
+ 	      int low_offset = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG ? 4 : 0;
+ 	      unsigned long regval;
+ 
+ 	      /* Write the low word of the double to the even register(s).  */
+ 	      regval = extract_unsigned_integer (val + low_offset, 4);
+ 	      if (mips_debug)
+ 		fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
+ 				    float_argreg, phex (regval, 4));
+ 	      write_register (float_argreg++, regval);
+ 	      if (mips_debug)
+ 		fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
+ 				    argreg, phex (regval, 4));
+ 	      write_register (argreg++, regval);
+ 
+ 	      /* Write the high word of the double to the odd register(s).  */
+ 	      regval = extract_unsigned_integer (val + 4 - low_offset, 4);
+ 	      if (mips_debug)
+ 		fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
+ 				    float_argreg, phex (regval, 4));
+ 	      write_register (float_argreg++, regval);
+ 
+ 	      if (mips_debug)
+ 		fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
+ 				    argreg, phex (regval, 4));
+ 	      write_register (argreg++, regval);
+ 	    }
+ 	  else
+ 	    {
+ 	      /* This is a floating point value that fits entirely
+ 	         in a single register.  */
+ 	      /* On 32 bit ABI's the float_argreg is further adjusted
+                  above to ensure that it is even register aligned.  */
+ 	      LONGEST regval = extract_unsigned_integer (val, len);
+ 	      if (mips_debug)
+ 		fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
+ 				    float_argreg, phex (regval, len));
+ 	      write_register (float_argreg++, regval);
+ 	      /* CAGNEY: 32 bit MIPS ABI's always reserve two FP
+ 		 registers for each argument.  The below is (my
+ 		 guess) to ensure that the corresponding integer
+ 		 register has reserved the same space.  */
+ 	      if (mips_debug)
+ 		fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
+ 				    argreg, phex (regval, len));
+ 	      write_register (argreg, regval);
+ 	      argreg += FP_REGISTER_DOUBLE ? 1 : 2;
+ 	    }
+ 	  /* Reserve space for the FP register.  */
+ 	  stack_offset += ROUND_UP (len, MIPS_STACK_ARGSIZE);
+ 	}
+       else
+ 	{
+ 	  /* Copy the argument to general registers or the stack in
+ 	     register-sized pieces.  Large arguments are split between
+ 	     registers and stack.  */
+ 	  /* Note: structs whose size is not a multiple of MIPS_REGSIZE
+ 	     are treated specially: Irix cc passes them in registers
+ 	     where gcc sometimes puts them on the stack.  For maximum
+ 	     compatibility, we will put them in both places.  */
+ 	  int odd_sized_struct = ((len > MIPS_SAVED_REGSIZE) &&
+ 				  (len % MIPS_SAVED_REGSIZE != 0));
+ 	  /* Structures should be aligned to eight bytes (even arg registers)
+ 	     on MIPS_ABI_O32, if their first member has double precision.  */
+ 	  if (MIPS_SAVED_REGSIZE < 8
+ 	      && mips_type_needs_double_align (arg_type))
+ 	    {
+ 	      if ((argreg & 1))
+ 	        argreg++;
+ 	    }
+ 	  /* Note: Floating-point values that didn't fit into an FP
+              register are only written to memory.  */
+ 	  while (len > 0)
+ 	    {
+ 	      /* Remember if the argument was written to the stack.  */
+ 	      int stack_used_p = 0;
+ 	      int partial_len = 
+ 		len < MIPS_SAVED_REGSIZE ? len : MIPS_SAVED_REGSIZE;
+ 
+ 	      if (mips_debug)
+ 		fprintf_unfiltered (gdb_stdlog, " -- partial=%d",
+ 				    partial_len);
+ 
+ 	      /* Write this portion of the argument to the stack.  */
+ 	      if (argreg > MIPS_LAST_ARG_REGNUM
+ 		  || odd_sized_struct
+ 		  || fp_register_arg_p (typecode, arg_type))
+ 		{
+ 		  /* Should shorter than int integer values be
+ 		     promoted to int before being stored? */
+ 		  int longword_offset = 0;
+ 		  CORE_ADDR addr;
+ 		  stack_used_p = 1;
+ 		  if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+ 		    {
+ 		      if (MIPS_STACK_ARGSIZE == 8 &&
+ 			  (typecode == TYPE_CODE_INT ||
+ 			   typecode == TYPE_CODE_PTR ||
+ 			   typecode == TYPE_CODE_FLT) && len <= 4)
+ 			longword_offset = MIPS_STACK_ARGSIZE - len;
+ 		    }
+ 
+ 		  if (mips_debug)
+ 		    {
+ 		      fprintf_unfiltered (gdb_stdlog, " - stack_offset=0x%s",
+ 					  paddr_nz (stack_offset));
+ 		      fprintf_unfiltered (gdb_stdlog, " longword_offset=0x%s",
+ 					  paddr_nz (longword_offset));
+ 		    }
+ 
+ 		  addr = sp + stack_offset + longword_offset;
+ 
+ 		  if (mips_debug)
+ 		    {
+ 		      int i;
+ 		      fprintf_unfiltered (gdb_stdlog, " @0x%s ", 
+ 					  paddr_nz (addr));
+ 		      for (i = 0; i < partial_len; i++)
+ 			{
+ 			  fprintf_unfiltered (gdb_stdlog, "%02x", 
+ 					      val[i] & 0xff);
+ 			}
+ 		    }
+ 		  write_memory (addr, val, partial_len);
+ 		}
+ 
+ 	      /* Note!!! This is NOT an else clause.  Odd sized
+ 	         structs may go thru BOTH paths.  Floating point
+ 	         arguments will not.  */
+ 	      /* Write this portion of the argument to a general
+                  purpose register.  */
+ 	      if (argreg <= MIPS_LAST_ARG_REGNUM
+ 		  && !fp_register_arg_p (typecode, arg_type))
+ 		{
+ 		  LONGEST regval = extract_signed_integer (val, partial_len);
+ 		  /* Value may need to be sign extended, because 
+ 		     MIPS_REGSIZE != MIPS_SAVED_REGSIZE.  */
+ 
+ 		  /* A non-floating-point argument being passed in a
+ 		     general register.  If a struct or union, and if
+ 		     the remaining length is smaller than the register
+ 		     size, we have to adjust the register value on
+ 		     big endian targets.
+ 
+ 		     It does not seem to be necessary to do the
+ 		     same for integral types.
+ 
+ 		     Also don't do this adjustment on O64 binaries.
+ 
+ 		     cagney/2001-07-23: gdb/179: Also, GCC, when
+ 		     outputting LE O32 with sizeof (struct) <
+ 		     MIPS_SAVED_REGSIZE, generates a left shift as
+ 		     part of storing the argument in a register a
+ 		     register (the left shift isn't generated when
+ 		     sizeof (struct) >= MIPS_SAVED_REGSIZE).  Since it
+ 		     is quite possible that this is GCC contradicting
+ 		     the LE/O32 ABI, GDB has not been adjusted to
+ 		     accommodate this.  Either someone needs to
+ 		     demonstrate that the LE/O32 ABI specifies such a
+ 		     left shift OR this new ABI gets identified as
+ 		     such and GDB gets tweaked accordingly.  */
+ 
+ 		  if (MIPS_SAVED_REGSIZE < 8
+ 		      && TARGET_BYTE_ORDER == BFD_ENDIAN_BIG
+ 		      && partial_len < MIPS_SAVED_REGSIZE
+ 		      && (typecode == TYPE_CODE_STRUCT ||
+ 			  typecode == TYPE_CODE_UNION))
+ 		    regval <<= ((MIPS_SAVED_REGSIZE - partial_len) *
+ 				TARGET_CHAR_BIT);
+ 
+ 		  if (mips_debug)
+ 		    fprintf_filtered (gdb_stdlog, " - reg=%d val=%s",
+ 				      argreg,
+ 				      phex (regval, MIPS_SAVED_REGSIZE));
+ 		  write_register (argreg, regval);
+ 		  argreg++;
+ 
+ 		  /* Prevent subsequent floating point arguments from
+ 		     being passed in floating point registers.  */
+ 		  float_argreg = MIPS_LAST_FP_ARG_REGNUM + 1;
+ 		}
+ 
+ 	      len -= partial_len;
+ 	      val += partial_len;
+ 
+ 	      /* Compute the the offset into the stack at which we
+ 		 will copy the next parameter.
+ 
+ 		 In older ABIs, the caller reserved space for
+ 		 registers that contained arguments.  This was loosely
+ 		 refered to as their "home".  Consequently, space is
+ 		 always allocated.  */
+ 
+ 	      stack_offset += ROUND_UP (partial_len, MIPS_STACK_ARGSIZE);
+ 	    }
+ 	}
+       if (mips_debug)
+ 	fprintf_unfiltered (gdb_stdlog, "\n");
+     }
+ 
+   /* Return adjusted stack pointer.  */
+   return sp;
+ }
+ 
  CORE_ADDR
  mips_push_return_address (CORE_ADDR pc, CORE_ADDR sp)
  {
*************** mips_gdbarch_init (struct gdbarch_info i
*** 4775,4781 ****
    switch (mips_abi)
      {
      case MIPS_ABI_O32:
!       set_gdbarch_push_arguments (gdbarch, mips_push_arguments);
        tdep->mips_default_saved_regsize = 4;
        tdep->mips_default_stack_argsize = 4;
        tdep->mips_fp_register_double = 0;
--- 5076,5082 ----
    switch (mips_abi)
      {
      case MIPS_ABI_O32:
!       set_gdbarch_push_arguments (gdbarch, mips_o32o64_push_arguments);
        tdep->mips_default_saved_regsize = 4;
        tdep->mips_default_stack_argsize = 4;
        tdep->mips_fp_register_double = 0;
*************** mips_gdbarch_init (struct gdbarch_info i
*** 4793,4799 ****
  					 mips_o32_use_struct_convention);
        break;
      case MIPS_ABI_O64:
!       set_gdbarch_push_arguments (gdbarch, mips_push_arguments);
        tdep->mips_default_saved_regsize = 8;
        tdep->mips_default_stack_argsize = 8;
        tdep->mips_fp_register_double = 1;
--- 5094,5100 ----
  					 mips_o32_use_struct_convention);
        break;
      case MIPS_ABI_O64:
!       set_gdbarch_push_arguments (gdbarch, mips_o32o64_push_arguments);
        tdep->mips_default_saved_regsize = 8;
        tdep->mips_default_stack_argsize = 8;
        tdep->mips_fp_register_double = 1;

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