Blame SOURCES/gdb-rhbz1182151-ibm-z13-14of22.patch

0b42f8
commit 80f75320167acb66486124c6b03e715596e9c789
0b42f8
Author: Andreas Arnez <arnez@linux.vnet.ibm.com>
0b42f8
Date:   Mon Apr 27 11:38:46 2015 +0200
0b42f8
0b42f8
    S390: Restructure s390_push_dummy_call
0b42f8
    
0b42f8
    Simplify the structure of s390_push_dummy_call and its various helper
0b42f8
    functions.  This reduces the code and makes it easier to extend.  The
0b42f8
    new code should be functionally equivalent to the old one, except that
0b42f8
    copies created by the caller are now always aligned on an 8-byte
0b42f8
    boundary.
0b42f8
    
0b42f8
    gdb/ChangeLog:
0b42f8
    
0b42f8
    	* s390-linux-tdep.c
0b42f8
    	(is_float_singleton): Remove function.  Move the "singleton" part
0b42f8
    	of the logic...
0b42f8
    	(s390_effective_inner_type): ...here.  New function.
0b42f8
    	(is_float_like): Remove function.  Inline its logic...
0b42f8
    	(s390_function_arg_float): ...here.
0b42f8
    	(is_pointer_like, is_integer_like, is_struct_like): Remove
0b42f8
    	functions.  Inline their logic...
0b42f8
    	(s390_function_arg_integer): ...here.
0b42f8
    	(s390_function_arg_pass_by_reference): Remove function.
0b42f8
    	(extend_simple_arg): Remove function.
0b42f8
    	(alignment_of): Remove function.
0b42f8
    	(struct s390_arg_state): New structure.
0b42f8
    	(s390_handle_arg): New function.
0b42f8
    	(s390_push_dummy_call): Move parameter placement logic to the new
0b42f8
    	function s390_handle_arg.  Call it for calculating the stack area
0b42f8
    	sizes first, and again for actually writing the parameters.
0b42f8
0b42f8
### a/gdb/ChangeLog
0b42f8
### b/gdb/ChangeLog
0b42f8
## -1,5 +1,25 @@
0b42f8
 2015-04-27  Andreas Arnez  <arnez@linux.vnet.ibm.com>
0b42f8
 
0b42f8
+	* s390-linux-tdep.c
0b42f8
+	(is_float_singleton): Remove function.  Move the "singleton" part
0b42f8
+	of the logic...
0b42f8
+	(s390_effective_inner_type): ...here.  New function.
0b42f8
+	(is_float_like): Remove function.  Inline its logic...
0b42f8
+	(s390_function_arg_float): ...here.
0b42f8
+	(is_pointer_like, is_integer_like, is_struct_like): Remove
0b42f8
+	functions.  Inline their logic...
0b42f8
+	(s390_function_arg_integer): ...here.
0b42f8
+	(s390_function_arg_pass_by_reference): Remove function.
0b42f8
+	(extend_simple_arg): Remove function.
0b42f8
+	(alignment_of): Remove function.
0b42f8
+	(struct s390_arg_state): New structure.
0b42f8
+	(s390_handle_arg): New function.
0b42f8
+	(s390_push_dummy_call): Move parameter placement logic to the new
0b42f8
+	function s390_handle_arg.  Call it for calculating the stack area
0b42f8
+	sizes first, and again for actually writing the parameters.
0b42f8
+
0b42f8
+2015-04-27  Andreas Arnez  <arnez@linux.vnet.ibm.com>
0b42f8
+
0b42f8
 	* s390-linux-tdep.c (is_power_of_two): Add comment.  Return
0b42f8
 	  false if the argument is zero.
0b42f8
 
0b42f8
Index: gdb-7.6.1/gdb/s390-tdep.c
0b42f8
===================================================================
0b42f8
--- gdb-7.6.1.orig/gdb/s390-tdep.c	2016-02-21 22:27:47.198043364 +0100
0b42f8
+++ gdb-7.6.1/gdb/s390-tdep.c	2016-02-21 22:30:02.980113202 +0100
0b42f8
@@ -2546,99 +2546,40 @@
0b42f8
 
0b42f8
 /* Dummy function calls.  */
0b42f8
 
0b42f8
-/* Return non-zero if TYPE is an integer-like type, zero otherwise.
0b42f8
-   "Integer-like" types are those that should be passed the way
0b42f8
-   integers are: integers, enums, ranges, characters, and booleans.  */
0b42f8
-static int
0b42f8
-is_integer_like (struct type *type)
0b42f8
-{
0b42f8
-  enum type_code code = TYPE_CODE (type);
0b42f8
+/* Unwrap any single-field structs in TYPE and return the effective
0b42f8
+   "inner" type.  E.g., yield "float" for all these cases:
0b42f8
 
0b42f8
-  return (code == TYPE_CODE_INT
0b42f8
-          || code == TYPE_CODE_ENUM
0b42f8
-          || code == TYPE_CODE_RANGE
0b42f8
-          || code == TYPE_CODE_CHAR
0b42f8
-          || code == TYPE_CODE_BOOL);
0b42f8
-}
0b42f8
+     float x;
0b42f8
+     struct { float x };
0b42f8
+     struct { struct { float x; } x; };
0b42f8
+     struct { struct { struct { float x; } x; } x; };  */
0b42f8
 
0b42f8
-/* Return non-zero if TYPE is a pointer-like type, zero otherwise.
0b42f8
-   "Pointer-like" types are those that should be passed the way
0b42f8
-   pointers are: pointers and references.  */
0b42f8
-static int
0b42f8
-is_pointer_like (struct type *type)
0b42f8
+static struct type *
0b42f8
+s390_effective_inner_type (struct type *type)
0b42f8
 {
0b42f8
-  enum type_code code = TYPE_CODE (type);
0b42f8
-
0b42f8
-  return (code == TYPE_CODE_PTR
0b42f8
-          || code == TYPE_CODE_REF);
0b42f8
+  while (TYPE_CODE (type) == TYPE_CODE_STRUCT
0b42f8
+	 && TYPE_NFIELDS (type) == 1)
0b42f8
+    type = check_typedef (TYPE_FIELD_TYPE (type, 0));
0b42f8
+  return type;
0b42f8
 }
0b42f8
 
0b42f8
+/* Return non-zero if TYPE should be passed like "float" or
0b42f8
+   "double".  */
0b42f8
 
0b42f8
-/* Return non-zero if TYPE is a `float singleton' or `double
0b42f8
-   singleton', zero otherwise.
0b42f8
-
0b42f8
-   A `T singleton' is a struct type with one member, whose type is
0b42f8
-   either T or a `T singleton'.  So, the following are all float
0b42f8
-   singletons:
0b42f8
-
0b42f8
-   struct { float x };
0b42f8
-   struct { struct { float x; } x; };
0b42f8
-   struct { struct { struct { float x; } x; } x; };
0b42f8
-
0b42f8
-   ... and so on.
0b42f8
-
0b42f8
-   All such structures are passed as if they were floats or doubles,
0b42f8
-   as the (revised) ABI says.  */
0b42f8
 static int
0b42f8
-is_float_singleton (struct type *type)
0b42f8
-{
0b42f8
-  if (TYPE_CODE (type) == TYPE_CODE_STRUCT && TYPE_NFIELDS (type) == 1)
0b42f8
-    {
0b42f8
-      struct type *singleton_type = TYPE_FIELD_TYPE (type, 0);
0b42f8
-      CHECK_TYPEDEF (singleton_type);
0b42f8
-
0b42f8
-      return (TYPE_CODE (singleton_type) == TYPE_CODE_FLT
0b42f8
-	      || TYPE_CODE (singleton_type) == TYPE_CODE_DECFLOAT
0b42f8
-	      || is_float_singleton (singleton_type));
0b42f8
-    }
0b42f8
-
0b42f8
-  return 0;
0b42f8
-}
0b42f8
-
0b42f8
-
0b42f8
-/* Return non-zero if TYPE is a struct-like type, zero otherwise.
0b42f8
-   "Struct-like" types are those that should be passed as structs are:
0b42f8
-   structs and unions.
0b42f8
-
0b42f8
-   As an odd quirk, not mentioned in the ABI, GCC passes float and
0b42f8
-   double singletons as if they were a plain float, double, etc.  (The
0b42f8
-   corresponding union types are handled normally.)  So we exclude
0b42f8
-   those types here.  *shrug* */
0b42f8
-static int
0b42f8
-is_struct_like (struct type *type)
0b42f8
+s390_function_arg_float (struct type *type)
0b42f8
 {
0b42f8
-  enum type_code code = TYPE_CODE (type);
0b42f8
-
0b42f8
-  return (code == TYPE_CODE_UNION
0b42f8
-          || (code == TYPE_CODE_STRUCT && ! is_float_singleton (type)));
0b42f8
-}
0b42f8
+  /* Note that long double as well as complex types are intentionally
0b42f8
+     excluded. */
0b42f8
+  if (TYPE_LENGTH (type) > 8)
0b42f8
+    return 0;
0b42f8
 
0b42f8
+  /* A struct containing just a float or double is passed like a float
0b42f8
+     or double.  */
0b42f8
+  type = s390_effective_inner_type (type);
0b42f8
 
0b42f8
-/* Return non-zero if TYPE is a float-like type, zero otherwise.
0b42f8
-   "Float-like" types are those that should be passed as
0b42f8
-   floating-point values are.
0b42f8
-
0b42f8
-   You'd think this would just be floats, doubles, long doubles, etc.
0b42f8
-   But as an odd quirk, not mentioned in the ABI, GCC passes float and
0b42f8
-   double singletons as if they were a plain float, double, etc.  (The
0b42f8
-   corresponding union types are handled normally.)  So we include
0b42f8
-   those types here.  *shrug* */
0b42f8
-static int
0b42f8
-is_float_like (struct type *type)
0b42f8
-{
0b42f8
   return (TYPE_CODE (type) == TYPE_CODE_FLT
0b42f8
-	  || TYPE_CODE (type) == TYPE_CODE_DECFLOAT
0b42f8
-          || is_float_singleton (type));
0b42f8
+	  || TYPE_CODE (type) == TYPE_CODE_DECFLOAT);
0b42f8
 }
0b42f8
 
0b42f8
 /* Determine whether N is a power of two.  */
0b42f8
@@ -2649,101 +2590,172 @@
0b42f8
   return n && ((n & (n - 1)) == 0);
0b42f8
 }
0b42f8
 
0b42f8
-/* Return non-zero if TYPE should be passed as a pointer to a copy,
0b42f8
-   zero otherwise.  */
0b42f8
-static int
0b42f8
-s390_function_arg_pass_by_reference (struct type *type)
0b42f8
-{
0b42f8
-  if (TYPE_LENGTH (type) > 8)
0b42f8
-    return 1;
0b42f8
-
0b42f8
-  return (is_struct_like (type) && !is_power_of_two (TYPE_LENGTH (type)))
0b42f8
-	  || TYPE_CODE (type) == TYPE_CODE_COMPLEX
0b42f8
-	  || (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type));
0b42f8
-}
0b42f8
-
0b42f8
-/* Return non-zero if TYPE should be passed in a float register
0b42f8
-   if possible.  */
0b42f8
-static int
0b42f8
-s390_function_arg_float (struct type *type)
0b42f8
-{
0b42f8
-  if (TYPE_LENGTH (type) > 8)
0b42f8
-    return 0;
0b42f8
-
0b42f8
-  return is_float_like (type);
0b42f8
-}
0b42f8
+/* For an argument whose type is TYPE and which is not passed like a
0b42f8
+   float, return non-zero if it should be passed like "int" or "long
0b42f8
+   long".  */
0b42f8
 
0b42f8
-/* Return non-zero if TYPE should be passed in an integer register
0b42f8
-   (or a pair of integer registers) if possible.  */
0b42f8
 static int
0b42f8
 s390_function_arg_integer (struct type *type)
0b42f8
 {
0b42f8
+  enum type_code code = TYPE_CODE (type);
0b42f8
+
0b42f8
   if (TYPE_LENGTH (type) > 8)
0b42f8
     return 0;
0b42f8
 
0b42f8
-   return is_integer_like (type)
0b42f8
-	  || is_pointer_like (type)
0b42f8
-	  || (is_struct_like (type) && is_power_of_two (TYPE_LENGTH (type)));
0b42f8
-}
0b42f8
-
0b42f8
-/* Return ARG, a `SIMPLE_ARG', sign-extended or zero-extended to a full
0b42f8
-   word as required for the ABI.  */
0b42f8
-static LONGEST
0b42f8
-extend_simple_arg (struct gdbarch *gdbarch, struct value *arg)
0b42f8
-{
0b42f8
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
0b42f8
-  struct type *type = check_typedef (value_type (arg));
0b42f8
+  if (code == TYPE_CODE_INT
0b42f8
+      || code == TYPE_CODE_ENUM
0b42f8
+      || code == TYPE_CODE_RANGE
0b42f8
+      || code == TYPE_CODE_CHAR
0b42f8
+      || code == TYPE_CODE_BOOL
0b42f8
+      || code == TYPE_CODE_PTR
0b42f8
+      || code == TYPE_CODE_REF)
0b42f8
+    return 1;
0b42f8
 
0b42f8
-  /* Even structs get passed in the least significant bits of the
0b42f8
-     register / memory word.  It's not really right to extract them as
0b42f8
-     an integer, but it does take care of the extension.  */
0b42f8
-  if (TYPE_UNSIGNED (type))
0b42f8
-    return extract_unsigned_integer (value_contents (arg),
0b42f8
-                                     TYPE_LENGTH (type), byte_order);
0b42f8
-  else
0b42f8
-    return extract_signed_integer (value_contents (arg),
0b42f8
-                                   TYPE_LENGTH (type), byte_order);
0b42f8
+  return ((code == TYPE_CODE_UNION || code == TYPE_CODE_STRUCT)
0b42f8
+	  && is_power_of_two (TYPE_LENGTH (type)));
0b42f8
 }
0b42f8
 
0b42f8
+/* Argument passing state: Internal data structure passed to helper
0b42f8
+   routines of s390_push_dummy_call.  */
0b42f8
 
0b42f8
-/* Return the alignment required by TYPE.  */
0b42f8
-static int
0b42f8
-alignment_of (struct type *type)
0b42f8
+struct s390_arg_state
0b42f8
+  {
0b42f8
+    /* Register cache, or NULL, if we are in "preparation mode".  */
0b42f8
+    struct regcache *regcache;
0b42f8
+    /* Next available general/floating-point register for argument
0b42f8
+       passing.  */
0b42f8
+    int gr, fr;
0b42f8
+    /* Current pointer to copy area (grows downwards).  */
0b42f8
+    CORE_ADDR copy;
0b42f8
+    /* Current pointer to parameter area (grows upwards).  */
0b42f8
+    CORE_ADDR argp;
0b42f8
+  };
0b42f8
+
0b42f8
+/* Prepare one argument ARG for a dummy call and update the argument
0b42f8
+   passing state AS accordingly.  If the regcache field in AS is set,
0b42f8
+   operate in "write mode" and write ARG into the inferior.  Otherwise
0b42f8
+   run "preparation mode" and skip all updates to the inferior.  */
0b42f8
+
0b42f8
+static void
0b42f8
+s390_handle_arg (struct s390_arg_state *as, struct value *arg,
0b42f8
+		 struct gdbarch_tdep *tdep, int word_size,
0b42f8
+		 enum bfd_endian byte_order)
0b42f8
 {
0b42f8
-  int alignment;
0b42f8
+  struct type *type = check_typedef (value_type (arg));
0b42f8
+  ULONGEST length = TYPE_LENGTH (type);
0b42f8
+  int write_mode = as->regcache != NULL;
0b42f8
 
0b42f8
-  if (is_integer_like (type)
0b42f8
-      || is_pointer_like (type)
0b42f8
-      || TYPE_CODE (type) == TYPE_CODE_FLT
0b42f8
-      || TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
0b42f8
-    alignment = TYPE_LENGTH (type);
0b42f8
-  else if (TYPE_CODE (type) == TYPE_CODE_STRUCT
0b42f8
-           || TYPE_CODE (type) == TYPE_CODE_UNION)
0b42f8
+  if (s390_function_arg_float (type))
0b42f8
+    {
0b42f8
+      /* The GNU/Linux for S/390 ABI uses FPRs 0 and 2 to pass
0b42f8
+	 arguments.  The GNU/Linux for zSeries ABI uses 0, 2, 4, and
0b42f8
+	 6.  */
0b42f8
+      if (as->fr <= (tdep->abi == ABI_LINUX_S390 ? 2 : 6))
0b42f8
+	{
0b42f8
+	  /* When we store a single-precision value in an FP register,
0b42f8
+	     it occupies the leftmost bits.  */
0b42f8
+	  if (write_mode)
0b42f8
+	    regcache_cooked_write_part (as->regcache,
0b42f8
+					S390_F0_REGNUM + as->fr,
0b42f8
+					0, length,
0b42f8
+					value_contents (arg));
0b42f8
+	  as->fr += 2;
0b42f8
+	}
0b42f8
+      else
0b42f8
+	{
0b42f8
+	  /* When we store a single-precision value in a stack slot,
0b42f8
+	     it occupies the rightmost bits.  */
0b42f8
+	  as->argp = align_up (as->argp + length, word_size);
0b42f8
+	  if (write_mode)
0b42f8
+	    write_memory (as->argp - length, value_contents (arg),
0b42f8
+			  length);
0b42f8
+	}
0b42f8
+    }
0b42f8
+  else if (s390_function_arg_integer (type) && length <= word_size)
0b42f8
     {
0b42f8
-      int i;
0b42f8
+      ULONGEST val;
0b42f8
 
0b42f8
-      alignment = 1;
0b42f8
-      for (i = 0; i < TYPE_NFIELDS (type); i++)
0b42f8
+      if (write_mode)
0b42f8
         {
0b42f8
-          int field_alignment
0b42f8
-	    = alignment_of (check_typedef (TYPE_FIELD_TYPE (type, i)));
0b42f8
-
0b42f8
-          if (field_alignment > alignment)
0b42f8
-            alignment = field_alignment;
0b42f8
+	  /* Place value in least significant bits of the register or
0b42f8
+	     memory word and sign- or zero-extend to full word size.
0b42f8
+	     This also applies to a struct or union.  */
0b42f8
+	  val = TYPE_UNSIGNED (type)
0b42f8
+	    ? extract_unsigned_integer (value_contents (arg),
0b42f8
+					length, byte_order)
0b42f8
+	    : extract_signed_integer (value_contents (arg),
0b42f8
+				      length, byte_order);
0b42f8
+	}
0b42f8
+
0b42f8
+      if (as->gr <= 6)
0b42f8
+	{
0b42f8
+	  if (write_mode)
0b42f8
+	    regcache_cooked_write_unsigned (as->regcache,
0b42f8
+					    S390_R0_REGNUM + as->gr,
0b42f8
+					    val);
0b42f8
+	  as->gr++;
0b42f8
+	}
0b42f8
+      else
0b42f8
+	{
0b42f8
+	  if (write_mode)
0b42f8
+	    write_memory_unsigned_integer (as->argp, word_size,
0b42f8
+					   byte_order, val);
0b42f8
+	  as->argp += word_size;
0b42f8
         }
0b42f8
     }
0b42f8
+  else if (s390_function_arg_integer (type) && length == 8)
0b42f8
+    {
0b42f8
+      if (as->gr <= 5)
0b42f8
+	{
0b42f8
+	  if (write_mode)
0b42f8
+	    {
0b42f8
+	      regcache_cooked_write (as->regcache,
0b42f8
+				     S390_R0_REGNUM + as->gr,
0b42f8
+				     value_contents (arg));
0b42f8
+	      regcache_cooked_write (as->regcache,
0b42f8
+				     S390_R0_REGNUM + as->gr + 1,
0b42f8
+				     value_contents (arg) + word_size);
0b42f8
+	    }
0b42f8
+	  as->gr += 2;
0b42f8
+	}
0b42f8
+      else
0b42f8
+	{
0b42f8
+	  /* If we skipped r6 because we couldn't fit a DOUBLE_ARG
0b42f8
+	     in it, then don't go back and use it again later.  */
0b42f8
+	  as->gr = 7;
0b42f8
+
0b42f8
+	  if (write_mode)
0b42f8
+	    write_memory (as->argp, value_contents (arg), length);
0b42f8
+	  as->argp += length;
0b42f8
+	}
0b42f8
+    }
0b42f8
   else
0b42f8
-    alignment = 1;
0b42f8
-
0b42f8
-  /* Check that everything we ever return is a power of two.  Lots of
0b42f8
-     code doesn't want to deal with aligning things to arbitrary
0b42f8
-     boundaries.  */
0b42f8
-  gdb_assert ((alignment & (alignment - 1)) == 0);
0b42f8
-
0b42f8
-  return alignment;
0b42f8
+    {
0b42f8
+      /* This argument type is never passed in registers.  Place the
0b42f8
+	 value in the copy area and pass a pointer to it.  Use 8-byte
0b42f8
+	 alignment as a conservative assumption.  */
0b42f8
+      as->copy = align_down (as->copy - length, 8);
0b42f8
+      if (write_mode)
0b42f8
+	write_memory (as->copy, value_contents (arg), length);
0b42f8
+
0b42f8
+      if (as->gr <= 6)
0b42f8
+	{
0b42f8
+	  if (write_mode)
0b42f8
+	    regcache_cooked_write_unsigned (as->regcache,
0b42f8
+					    S390_R0_REGNUM + as->gr,
0b42f8
+					    as->copy);
0b42f8
+	  as->gr++;
0b42f8
+	}
0b42f8
+      else
0b42f8
+	{
0b42f8
+	  if (write_mode)
0b42f8
+	    write_memory_unsigned_integer (as->argp, word_size,
0b42f8
+					   byte_order, as->copy);
0b42f8
+	  as->argp += word_size;
0b42f8
+	}
0b42f8
+    }
0b42f8
 }
0b42f8
 
0b42f8
-
0b42f8
 /* Put the actual parameter values pointed to by ARGS[0..NARGS-1] in
0b42f8
    place to be passed to a function, as specified by the "GNU/Linux
0b42f8
    for S/390 ELF Application Binary Interface Supplement".
0b42f8
@@ -2758,6 +2770,7 @@
0b42f8
 
0b42f8
    Our caller has taken care of any type promotions needed to satisfy
0b42f8
    prototypes or the old K&R argument-passing rules.  */
0b42f8
+
0b42f8
 static CORE_ADDR
0b42f8
 s390_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
0b42f8
 		      struct regcache *regcache, CORE_ADDR bp_addr,
0b42f8
@@ -2768,151 +2781,48 @@
0b42f8
   int word_size = gdbarch_ptr_bit (gdbarch) / 8;
0b42f8
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
0b42f8
   int i;
0b42f8
+  struct s390_arg_state arg_state, arg_prep;
0b42f8
+  CORE_ADDR param_area_start, new_sp;
0b42f8
 
0b42f8
-  /* If the i'th argument is passed as a reference to a copy, then
0b42f8
-     copy_addr[i] is the address of the copy we made.  */
0b42f8
-  CORE_ADDR *copy_addr = alloca (nargs * sizeof (CORE_ADDR));
0b42f8
+  arg_prep.copy = sp;
0b42f8
+  arg_prep.gr = struct_return ? 3 : 2;
0b42f8
+  arg_prep.fr = 0;
0b42f8
+  arg_prep.argp = 0;
0b42f8
+  arg_prep.regcache = NULL;
0b42f8
 
0b42f8
-  /* Reserve space for the reference-to-copy area.  */
0b42f8
-  for (i = 0; i < nargs; i++)
0b42f8
-    {
0b42f8
-      struct value *arg = args[i];
0b42f8
-      struct type *type = check_typedef (value_type (arg));
0b42f8
+  /* Initialize arg_state for "preparation mode".  */
0b42f8
+  arg_state = arg_prep;
0b42f8
 
0b42f8
-      if (s390_function_arg_pass_by_reference (type))
0b42f8
-        {
0b42f8
-          sp -= TYPE_LENGTH (type);
0b42f8
-          sp = align_down (sp, alignment_of (type));
0b42f8
-          copy_addr[i] = sp;
0b42f8
-        }
0b42f8
-    }
0b42f8
+  /* Update arg_state.copy with the start of the reference-to-copy area
0b42f8
+     and arg_state.argp with the size of the parameter area.  */
0b42f8
+  for (i = 0; i < nargs; i++)
0b42f8
+    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order);
0b42f8
 
0b42f8
-  /* Reserve space for the parameter area.  As a conservative
0b42f8
-     simplification, we assume that everything will be passed on the
0b42f8
-     stack.  Since every argument larger than 8 bytes will be 
0b42f8
-     passed by reference, we use this simple upper bound.  */
0b42f8
-  sp -= nargs * 8;
0b42f8
-
0b42f8
-  /* After all that, make sure it's still aligned on an eight-byte
0b42f8
-     boundary.  */
0b42f8
-  sp = align_down (sp, 8);
0b42f8
+  param_area_start = align_down (arg_state.copy - arg_state.argp, 8);
0b42f8
 
0b42f8
   /* Allocate the standard frame areas: the register save area, the
0b42f8
-     word reserved for the compiler (which seems kind of meaningless),
0b42f8
-     and the back chain pointer.  */
0b42f8
-  sp -= 16*word_size + 32;
0b42f8
-
0b42f8
-  /* Now we have the final SP value.  Make sure we didn't underflow;
0b42f8
-     on 31-bit, this would result in addresses with the high bit set,
0b42f8
-     which causes confusion elsewhere.  Note that if we error out
0b42f8
-     here, stack and registers remain untouched.  */
0b42f8
-  if (gdbarch_addr_bits_remove (gdbarch, sp) != sp)
0b42f8
+     word reserved for the compiler, and the back chain pointer.  */
0b42f8
+  new_sp = param_area_start - (16 * word_size + 32);
0b42f8
+
0b42f8
+  /* Now we have the final stack pointer.  Make sure we didn't
0b42f8
+     underflow; on 31-bit, this would result in addresses with the
0b42f8
+     high bit set, which causes confusion elsewhere.  Note that if we
0b42f8
+     error out here, stack and registers remain untouched.  */
0b42f8
+  if (gdbarch_addr_bits_remove (gdbarch, new_sp) != new_sp)
0b42f8
     error (_("Stack overflow"));
0b42f8
 
0b42f8
+  /* Pass the structure return address in general register 2.  */
0b42f8
+  if (struct_return)
0b42f8
+    regcache_cooked_write_unsigned (regcache, S390_R2_REGNUM, struct_addr);
0b42f8
+
0b42f8
+  /* Initialize arg_state for "write mode".  */
0b42f8
+  arg_state = arg_prep;
0b42f8
+  arg_state.argp = param_area_start;
0b42f8
+  arg_state.regcache = regcache;
0b42f8
 
0b42f8
-  /* Finally, place the actual parameters, working from SP towards
0b42f8
-     higher addresses.  The code above is supposed to reserve enough
0b42f8
-     space for this.  */
0b42f8
-  {
0b42f8
-    int fr = 0;
0b42f8
-    int gr = 2;
0b42f8
-    CORE_ADDR starg = sp + 16*word_size + 32;
0b42f8
-
0b42f8
-    /* A struct is returned using general register 2.  */
0b42f8
-    if (struct_return)
0b42f8
-      {
0b42f8
-	regcache_cooked_write_unsigned (regcache, S390_R0_REGNUM + gr,
0b42f8
-				        struct_addr);
0b42f8
-	gr++;
0b42f8
-      }
0b42f8
-
0b42f8
-    for (i = 0; i < nargs; i++)
0b42f8
-      {
0b42f8
-        struct value *arg = args[i];
0b42f8
-        struct type *type = check_typedef (value_type (arg));
0b42f8
-        ULONGEST length = TYPE_LENGTH (type);
0b42f8
-
0b42f8
-	if (s390_function_arg_pass_by_reference (type))
0b42f8
-	  {
0b42f8
-	    /* Actually copy the argument contents to the stack slot
0b42f8
-	       that was reserved above.  */
0b42f8
-	    write_memory (copy_addr[i], value_contents (arg), length);
0b42f8
-
0b42f8
-	    if (gr <= 6)
0b42f8
-	      {
0b42f8
-		regcache_cooked_write_unsigned (regcache, S390_R0_REGNUM + gr,
0b42f8
-					        copy_addr[i]);
0b42f8
-		gr++;
0b42f8
-	      }
0b42f8
-	    else
0b42f8
-	      {
0b42f8
-		write_memory_unsigned_integer (starg, word_size, byte_order,
0b42f8
-					       copy_addr[i]);
0b42f8
-		starg += word_size;
0b42f8
-	      }
0b42f8
-	  }
0b42f8
-	else if (s390_function_arg_float (type))
0b42f8
-	  {
0b42f8
-	    /* The GNU/Linux for S/390 ABI uses FPRs 0 and 2 to pass arguments,
0b42f8
-	       the GNU/Linux for zSeries ABI uses 0, 2, 4, and 6.  */
0b42f8
-	    if (fr <= (tdep->abi == ABI_LINUX_S390 ? 2 : 6))
0b42f8
-	      {
0b42f8
-		/* When we store a single-precision value in an FP register,
0b42f8
-		   it occupies the leftmost bits.  */
0b42f8
-		regcache_cooked_write_part (regcache, S390_F0_REGNUM + fr,
0b42f8
-					    0, length, value_contents (arg));
0b42f8
-		fr += 2;
0b42f8
-	      }
0b42f8
-	    else
0b42f8
-	      {
0b42f8
-		/* When we store a single-precision value in a stack slot,
0b42f8
-		   it occupies the rightmost bits.  */
0b42f8
-		starg = align_up (starg + length, word_size);
0b42f8
-                write_memory (starg - length, value_contents (arg), length);
0b42f8
-	      }
0b42f8
-	  }
0b42f8
-	else if (s390_function_arg_integer (type) && length <= word_size)
0b42f8
-	  {
0b42f8
-	    if (gr <= 6)
0b42f8
-	      {
0b42f8
-		/* Integer arguments are always extended to word size.  */
0b42f8
-		regcache_cooked_write_signed (regcache, S390_R0_REGNUM + gr,
0b42f8
-					      extend_simple_arg (gdbarch,
0b42f8
-								 arg));
0b42f8
-		gr++;
0b42f8
-	      }
0b42f8
-	    else
0b42f8
-	      {
0b42f8
-		/* Integer arguments are always extended to word size.  */
0b42f8
-		write_memory_signed_integer (starg, word_size, byte_order,
0b42f8
-                                             extend_simple_arg (gdbarch, arg));
0b42f8
-                starg += word_size;
0b42f8
-	      }
0b42f8
-	  }
0b42f8
-	else if (s390_function_arg_integer (type) && length == 2*word_size)
0b42f8
-	  {
0b42f8
-	    if (gr <= 5)
0b42f8
-	      {
0b42f8
-		regcache_cooked_write (regcache, S390_R0_REGNUM + gr,
0b42f8
-				       value_contents (arg));
0b42f8
-		regcache_cooked_write (regcache, S390_R0_REGNUM + gr + 1,
0b42f8
-				       value_contents (arg) + word_size);
0b42f8
-		gr += 2;
0b42f8
-	      }
0b42f8
-	    else
0b42f8
-	      {
0b42f8
-		/* If we skipped r6 because we couldn't fit a DOUBLE_ARG
0b42f8
-		   in it, then don't go back and use it again later.  */
0b42f8
-		gr = 7;
0b42f8
-
0b42f8
-		write_memory (starg, value_contents (arg), length);
0b42f8
-		starg += length;
0b42f8
-	      }
0b42f8
-	  }
0b42f8
-	else
0b42f8
-	  internal_error (__FILE__, __LINE__, _("unknown argument type"));
0b42f8
-      }
0b42f8
-  }
0b42f8
+  /* Write all parameters.  */
0b42f8
+  for (i = 0; i < nargs; i++)
0b42f8
+    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order);
0b42f8
 
0b42f8
   /* Store return PSWA.  In 31-bit mode, keep addressing mode bit.  */
0b42f8
   if (word_size == 4)
0b42f8
@@ -2924,11 +2834,11 @@
0b42f8
   regcache_cooked_write_unsigned (regcache, S390_RETADDR_REGNUM, bp_addr);
0b42f8
 
0b42f8
   /* Store updated stack pointer.  */
0b42f8
-  regcache_cooked_write_unsigned (regcache, S390_SP_REGNUM, sp);
0b42f8
+  regcache_cooked_write_unsigned (regcache, S390_SP_REGNUM, new_sp);
0b42f8
 
0b42f8
   /* We need to return the 'stack part' of the frame ID,
0b42f8
      which is actually the top of the register save area.  */
0b42f8
-  return sp + 16*word_size + 32;
0b42f8
+  return param_area_start;
0b42f8
 }
0b42f8
 
0b42f8
 /* Assuming THIS_FRAME is a dummy, return the frame ID of that