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]

PATCH: new S/390 argument-passing code



2001-11-13  Jim Blandy  <jimb@redhat.com>

	* s390-tdep.c: Rewrite inferior function call code.  This may
	break zSeries support; that should be fixed soon.
	#include "gdb_assert.h".
	(is_integer_like, is_pointer_like, is_simple_arg,
	pass_by_copy_ref, extend_simple_arg, is_double_arg, round_up,
	round_down, alignment_of): New functions.
	(s390_push_arguments): Rewritten to handle passing large arguments
	by value, and to make more readable.

Index: gdb/s390-tdep.c
===================================================================
RCS file: /cvs/cvsfiles/devo/gdb/s390-tdep.c,v
retrieving revision 2.11
diff -c -r2.11 s390-tdep.c
*** gdb/s390-tdep.c	2001/11/13 17:37:53	2.11
--- gdb/s390-tdep.c	2001/11/13 17:40:45
***************
*** 36,46 ****
  #include "floatformat.h"
  #include "regcache.h"
  #include "value.h"
  
  
  
  
- 
  /* Number of bytes of storage in the actual machine representation
     for register N. 
     Note that the unsigned cast here forces the result of the
--- 36,46 ----
  #include "floatformat.h"
  #include "regcache.h"
  #include "value.h"
+ #include "gdb_assert.h"
  
  
  
  
  /* Number of bytes of storage in the actual machine representation
     for register N. 
     Note that the unsigned cast here forces the result of the
***************
*** 1199,1314 ****
  }
  
  
! /* used by call function by hand
!   struct_return indicates that this function returns a structure &
!   therefore gpr2 stores a pointer to the structure to be returned as
!   opposed to the first argument.
!   Currently I haven't seen a TYPE_CODE_INT whose size wasn't 2^n or less
!   than S390_GPR_SIZE this is good because I don't seem to have to worry
!   about sign extending pushed arguments (i.e. a signed char currently
!   comes into this code with a size of 4 ). */
  
! CORE_ADDR
! s390_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
! 		     int struct_return, CORE_ADDR struct_addr)
  {
!   int num_float_args, num_gpr_args, orig_num_gpr_args, argno;
!   int second_pass, len, arglen, gprs_required;
!   CORE_ADDR outgoing_args_ptr, outgoing_args_space;
!   struct value *arg;
!   struct type *type;
!   int max_num_gpr_args = 5 - (struct_return ? 1 : 0);
!   int arg0_regnum = S390_GP0_REGNUM + 2 + (struct_return ? 1 : 0);
!   char *reg_buff = alloca (max (S390_FPR_SIZE, REGISTER_SIZE)), *value;
  
!   for (second_pass = 0; second_pass <= 1; second_pass++)
!     {
!       if (second_pass)
! 	outgoing_args_ptr = sp + S390_STACK_FRAME_OVERHEAD;
!       else
! 	outgoing_args_ptr = 0;
!       num_float_args = 0;
!       num_gpr_args = 0;
!       for (argno = 0; argno < nargs; argno++)
! 	{
! 	  arg = args[argno];
! 	  type = check_typedef (VALUE_TYPE (arg));
! 	  len = TYPE_LENGTH (type);
! 	  if (TYPE_CODE (type) == TYPE_CODE_FLT)
! 	    {
! 	      int all_float_registers_used =
! 		num_float_args > (GDB_TARGET_IS_ESAME ? 3 : 1);
  
- 	      if (second_pass)
- 		{
- 		  DOUBLEST tempfloat =
- 		    extract_floating (VALUE_CONTENTS (arg), len);
  
  
- 		  floatformat_from_doublest (all_float_registers_used &&
- 					     len == (TARGET_FLOAT_BIT >> 3)
- 					     ? &floatformat_ieee_single_big
- 					     : &floatformat_ieee_double_big,
- 					     &tempfloat, reg_buff);
- 		  if (all_float_registers_used)
- 		    write_memory (outgoing_args_ptr, reg_buff, len);
- 		  else
- 		    write_register_bytes (REGISTER_BYTE ((S390_FP0_REGNUM)
- 							 +
- 							 (2 *
- 							  num_float_args)),
- 					  reg_buff, S390_FPR_SIZE);
- 		}
- 	      if (all_float_registers_used)
- 		outgoing_args_ptr += len;
- 	      num_float_args++;
- 	    }
- 	  else
- 	    {
- 	      gprs_required = ((len + (S390_GPR_SIZE - 1)) / S390_GPR_SIZE);
  
! 	      value =
! 		s390_promote_integer_argument (type, VALUE_CONTENTS (arg),
! 					       reg_buff, &arglen);
! 
! 	      orig_num_gpr_args = num_gpr_args;
! 	      num_gpr_args += gprs_required;
! 	      if (num_gpr_args > max_num_gpr_args)
! 		{
! 		  if (second_pass)
! 		    write_memory (outgoing_args_ptr, value, arglen);
! 		  outgoing_args_ptr += arglen;
! 		}
! 	      else
! 		{
! 		  if (second_pass)
! 		    write_register_bytes (REGISTER_BYTE (arg0_regnum)
! 					  +
! 					  (orig_num_gpr_args * S390_GPR_SIZE),
! 					  value, arglen);
! 		}
! 	    }
! 	}
!       if (second_pass)
          {
!           /* Write the back chain pointer into the first word of the
!              stack frame.  This will help us get backtraces from
!              within functions called from GDB.  */
!           write_memory_unsigned_integer (sp, 
!                                          (TARGET_PTR_BIT / TARGET_CHAR_BIT),
!                                          read_fp ());
          }
!       else
! 	{
! 	  outgoing_args_space = outgoing_args_ptr;
! 	  /* Align to 16 bytes because because I like alignment & 
! 	     some of the kernel code requires 8 byte stack alignment at least. */
! 	  sp = (sp - (S390_STACK_FRAME_OVERHEAD + outgoing_args_ptr)) & (-16);
! 	}
  
      }
-   return sp;
  
  }
  
  /* Return the GDB type object for the "standard" data type
--- 1199,1541 ----
  }
  
  
! /* Return non-zero if TYPE is an integer-like type, zero otherwise.
!    "Integer-like" types are those that should be passed the way
!    integers are: integers, enums, ranges, characters, and booleans.  */
! static int
! is_integer_like (struct type *type)
! {
!   enum type_code code = TYPE_CODE (type);
  
!   return (code == TYPE_CODE_INT
!           || code == TYPE_CODE_ENUM
!           || code == TYPE_CODE_RANGE
!           || code == TYPE_CODE_CHAR
!           || code == TYPE_CODE_BOOL);
! }
! 
! 
! /* Return non-zero if TYPE is a pointer-like type, zero otherwise.
!    "Pointer-like" types are those that should be passed the way
!    pointers are: pointers and references.  */
! static int
! is_pointer_like (struct type *type)
  {
!   enum type_code code = TYPE_CODE (type);
  
!   return (code == TYPE_CODE_PTR
!           || code == TYPE_CODE_REF);
! }
  
  
+ /* Return non-zero if TYPE is considered a `DOUBLE_OR_FLOAT', as
+    defined by the parameter passing conventions described in the
+    "Linux for S/390 ELF Application Binary Interface Supplement".
+    Otherwise, return zero.  */
+ static int
+ is_double_or_float (struct type *type)
+ {
+   return (TYPE_CODE (type) == TYPE_CODE_FLT
+           && (TYPE_LENGTH (type) == 4
+               || TYPE_LENGTH (type) == 8));
+ }
  
  
! /* Return non-zero if TYPE is considered a `SIMPLE_ARG', as defined by
!    the parameter passing conventions described in the "Linux for S/390
!    ELF Application Binary Interface Supplement".  Return zero otherwise.  */
! static int
! is_simple_arg (struct type *type)
! {
!   enum type_code code = TYPE_CODE (type);
!   unsigned length = TYPE_LENGTH (type);
! 
!   return ((is_integer_like (type) && length <= 4)
!           || is_pointer_like (type)
!           || code == TYPE_CODE_STRUCT
!           || code == TYPE_CODE_UNION
!           || (code == TYPE_CODE_FLT && length == 16));
! }
! 
! 
! /* Return non-zero if TYPE should be passed as a pointer to a copy,
!    zero otherwise.  TYPE must be a SIMPLE_ARG, as recognized by
!    `is_simple_arg'.  */
! static int
! pass_by_copy_ref (struct type *type)
! {
!   enum type_code code = TYPE_CODE (type);
!   unsigned length = TYPE_LENGTH (type);
! 
!   return (((code == TYPE_CODE_STRUCT || code == TYPE_CODE_UNION)
!            && length != 1 && length != 2 && length != 4)
!           || (code == TYPE_CODE_FLT && length == 16));
! }
! 
! 
! /* Return ARG, a `SIMPLE_ARG', sign-extended or zero-extended to a full
!    word as required for the ABI.  */
! static LONGEST
! extend_simple_arg (struct value *arg)
! {
!   struct type *type = VALUE_TYPE (arg);
! 
!   /* Even structs get passed in the least significant bits of the
!      register / memory word.  It's not really right to extract them as
!      an integer, but it does take care of the extension.  */
!   if (TYPE_UNSIGNED (type))
!     return extract_unsigned_integer (VALUE_CONTENTS (arg),
!                                      TYPE_LENGTH (type));
!   else
!     return extract_signed_integer (VALUE_CONTENTS (arg),
!                                    TYPE_LENGTH (type));
! }
! 
! 
! /* Return non-zero if TYPE is a `DOUBLE_ARG', as defined by the
!    parameter passing conventions described in the "Linux for S/390 ELF
!    Application Binary Interface Supplement".  Return zero otherwise.  */
! static int
! is_double_arg (struct type *type)
! {
!   enum type_code code = TYPE_CODE (type);
!   unsigned length = TYPE_LENGTH (type);
! 
!   return ((is_integer_like (type)
!            || code == TYPE_CODE_STRUCT
!            || code == TYPE_CODE_UNION)
!           && length == 8);
! }
! 
! 
! /* Round ADDR up to the next N-byte boundary.  N must be a power of
!    two.  */
! static CORE_ADDR
! round_up (CORE_ADDR addr, int n)
! {
!   /* Check that N is really a power of two.  */
!   gdb_assert (n && (n & (n-1)) == 0);
!   return ((addr + n - 1) & -n);
! }
! 
! 
! /* Round ADDR down to the next N-byte boundary.  N must be a power of
!    two.  */
! static CORE_ADDR
! round_down (CORE_ADDR addr, int n)
! {
!   /* Check that N is really a power of two.  */
!   gdb_assert (n && (n & (n-1)) == 0);
!   return (addr & -n);
! }
! 
! 
! /* Return the alignment required by TYPE.  */
! static int
! alignment_of (struct type *type)
! {
!   int alignment;
! 
!   if (is_integer_like (type)
!       || is_pointer_like (type)
!       || TYPE_CODE (type) == TYPE_CODE_FLT)
!     alignment = TYPE_LENGTH (type);
!   else if (TYPE_CODE (type) == TYPE_CODE_STRUCT
!            || TYPE_CODE (type) == TYPE_CODE_UNION)
!     {
!       int i;
! 
!       alignment = 1;
!       for (i = 0; i < TYPE_NFIELDS (type); i++)
          {
!           int field_alignment = alignment_of (TYPE_FIELD_TYPE (type, i));
! 
!           if (field_alignment > alignment)
!             alignment = field_alignment;
          }
!     }
!   else
!     alignment = 1;
! 
!   /* Check that everything we ever return is a power of two.  Lots of
!      code doesn't want to deal with aligning things to arbitrary
!      boundaries.  */
!   gdb_assert ((alignment & (alignment - 1)) == 0);
! 
!   return alignment;
! }
! 
  
+ /* Put the actual parameter values pointed to by ARGS[0..NARGS-1] in
+    place to be passed to a function, as specified by the "Linux for
+    S/390 ELF Application Binary Interface Supplement".
+ 
+    SP is the current stack pointer.  We must put arguments, links,
+    padding, etc. whereever they belong, and return the new stack
+    pointer value.
+    
+    If STRUCT_RETURN is non-zero, then the function we're calling is
+    going to return a structure by value; STRUCT_ADDR is the address of
+    a block we've allocated for it on the stack.
+ 
+    Our caller has taken care of any type promotions needed to satisfy
+    prototypes or the old K&R argument-passing rules.  */
+ CORE_ADDR
+ s390_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
+ 		     int struct_return, CORE_ADDR struct_addr)
+ {
+   int i;
+   int pointer_size = (TARGET_PTR_BIT / TARGET_CHAR_BIT);
+ 
+   /* The number of arguments passed by reference-to-copy.  */
+   int num_copies;
+ 
+   /* If the i'th argument is passed as a reference to a copy, then
+      copy_addr[i] is the address of the copy we made.  */
+   CORE_ADDR *copy_addr = alloca (nargs * sizeof (CORE_ADDR));
+ 
+   /* Build the reference-to-copy area.  */
+   num_copies = 0;
+   for (i = 0; i < nargs; i++)
+     {
+       struct value *arg = args[i];
+       struct type *type = VALUE_TYPE (arg);
+       unsigned length = TYPE_LENGTH (type);
+ 
+       if (is_simple_arg (type)
+           && pass_by_copy_ref (type))
+         {
+           sp -= length;
+           sp = round_down (sp, alignment_of (type));
+           write_memory (sp, VALUE_CONTENTS (arg), length);
+           copy_addr[i] = sp;
+           num_copies++;
+         }
      }
  
+   /* Reserve space for the parameter area.  As a conservative
+      simplification, we assume that everything will be passed on the
+      stack.  */
+   {
+     int i;
+ 
+     for (i = 0; i < nargs; i++)
+       {
+         struct value *arg = args[i];
+         struct type *type = VALUE_TYPE (arg);
+         int length = TYPE_LENGTH (type);
+         
+         sp = round_down (sp, alignment_of (type));
+ 
+         /* SIMPLE_ARG values get extended to 32 bits.  Assume every
+            argument is.  */
+         if (length < 4) length = 4;
+         sp -= length;
+       }
+   }
+ 
+   /* Include space for any reference-to-copy pointers.  */
+   sp = round_down (sp, pointer_size);
+   sp -= num_copies * pointer_size;
+     
+   /* After all that, make sure it's still aligned on an eight-byte
+      boundary.  */
+   sp = round_down (sp, 8);
+ 
+   /* Finally, place the actual parameters, working from SP towards
+      higher addresses.  The code above is supposed to reserve enough
+      space for this.  */
+   {
+     int fr = 0;
+     int gr = 2;
+     CORE_ADDR starg = sp;
+ 
+     for (i = 0; i < nargs; i++)
+       {
+         struct value *arg = args[i];
+         struct type *type = VALUE_TYPE (arg);
+         
+         if (is_double_or_float (type)
+             && fr <= 2)
+           {
+             /* When we store a single-precision value in an FP register,
+                it occupies the leftmost bits.  */
+             write_register_bytes (REGISTER_BYTE (S390_FP0_REGNUM + fr),
+                                   VALUE_CONTENTS (arg),
+                                   TYPE_LENGTH (type));
+             fr += 2;
+           }
+         else if (is_simple_arg (type)
+                  && gr <= 6)
+           {
+             /* Do we need to pass a pointer to our copy of this
+                argument?  */
+             if (pass_by_copy_ref (type))
+               write_register (S390_GP0_REGNUM + gr, copy_addr[i]);
+             else
+               write_register (S390_GP0_REGNUM + gr, extend_simple_arg (arg));
+ 
+             gr++;
+           }
+         else if (is_double_arg (type)
+                  && gr <= 5)
+           {
+             write_register_gen (S390_GP0_REGNUM + gr,
+                                 VALUE_CONTENTS (arg));
+             write_register_gen (S390_GP0_REGNUM + gr + 1,
+                                 VALUE_CONTENTS (arg) + 4);
+             gr += 2;
+           }
+         else
+           {
+             /* The `OTHER' case.  */
+             enum type_code code = TYPE_CODE (type);
+             unsigned length = TYPE_LENGTH (type);
+             
+             /* If we skipped r6 because we couldn't fit a DOUBLE_ARG
+                in it, then don't go back and use it again later.  */
+             if (is_double_arg (type) && gr == 6)
+               gr = 7;
+ 
+             if (is_simple_arg (type))
+               {
+                 /* Simple args are always either extended to 32 bits,
+                    or pointers.  */
+                 starg = round_up (starg, 4);
+ 
+                 /* Do we need to pass a pointer to our copy of this
+                    argument?  */
+                 if (pass_by_copy_ref (type))
+                   write_memory_signed_integer (starg, pointer_size,
+                                                copy_addr[i]);
+                 else
+                   /* Simple args are always extended to 32 bits.  */
+                   write_memory_signed_integer (starg, 4,
+                                                extend_simple_arg (arg));
+                 starg += 4;
+               }
+             else
+               {
+                 starg = round_up (starg, alignment_of (type));
+                 write_memory (starg, VALUE_CONTENTS (arg), length);
+                 starg += length;
+               }
+           }
+       }
+   }
+ 
+   /* Allocate the standard frame areas: the register save area, the
+      word reserved for the compiler (which seems kind of meaningless),
+      and the back chain pointer.  */
+   sp -= 96;
+ 
+   /* Write the back chain pointer into the first word of the stack
+      frame.  This will help us get backtraces from within functions
+      called from GDB.  */
+   write_memory_unsigned_integer (sp, (TARGET_PTR_BIT / TARGET_CHAR_BIT),
+                                  read_fp ());
+ 
+   return sp;
  }
  
  /* Return the GDB type object for the "standard" data type


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