diff mbox

[4/4] S390: Vector ABI support

Message ID 87vbgl7ffh.fsf@br87z6lw.de.ibm.com
State New
Headers show

Commit Message

Andreas Arnez April 24, 2015, 2:45 p.m. UTC
On Thu, Apr 23 2015, Ulrich Weigand wrote:

> Andreas Arnez wrote:
>
> [...]
>
>>  static void
>>  s390_handle_arg (struct s390_arg_state *as, struct value *arg,
>>  		 struct gdbarch_tdep *tdep, int word_size,
>> -		 enum bfd_endian byte_order)
>> +		 enum bfd_endian byte_order, int is_vararg)
>
> Maybe "is_unnamed" would be better than "is_vararg".

OK, changed.

>> +  /* Determine vector ABI.  */
>> +  if (have_vx
>> +      && info.abfd != NULL
>> +      && info.abfd->format == bfd_object
>> +      && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour
>> +      && bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU,
>> +				   Tag_GNU_S390_ABI_Vector) == 2)
>
> I think this needs to be under an #ifdef HAVE_ELF, otherwise we'd
> get compile errors when building GDB on a non-ELF host system.

OK, added the #ifdef.

> Otherwise, this is OK once the binutils patch is committed.

Thanks for reviewing!  Updated patch below.

-- >8 --
Subject: [PATCH v2] S390: Vector ABI support

With the S390 vector ABI, vector registers are used for passing vector
arguments and for returning a vector.  Support this ABI in inferior
function calls and when setting or retrieving a function's return
value.

gdb/ChangeLog:

	* s390-linux-tdep.c: Include "elf/s390.h" and "elf-bfd.h".
	(enum s390_vector_abi_kind): New enum.
	(struct gdbarch_tdep)<vector_abi>: New field.
	(s390_effective_inner_type): Add parameter min_size.  Stop
	unwrapping if the inner type is smaller than min_size.
	(s390_function_arg_float): Adjust call to
	s390_effective_inner_type.
	(s390_function_arg_vector): New function.
	(s390_function_arg_integer): Adjust comment.
	(struct s390_arg_state)<vr>: New field.
	(s390_handle_arg): Add parameter 'is_unnamed'.  Pass vector
	arguments according to vector ABI when appropriate.
	(s390_push_dummy_call): Initialize the argument state's field
	'vr'.  Adjust calls to s390_handle_arg.
	(s390_register_return_value): Handle vector return values.
	(s390_return_value): Apply the "register" return value convention
	to a vector when appropriate.
	(s390_gdbarch_init): Initialize tdep->vector_abi.
	* NEWS: Announce S390 vector ABI support.
---
 gdb/NEWS              |   2 +
 gdb/s390-linux-tdep.c | 121 ++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 110 insertions(+), 13 deletions(-)
diff mbox

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 62cbdcb..b2c92b1 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -42,6 +42,8 @@ 
   (no "set sysroot" or "file" commands are required).  See "New remote
   packets" below.
 
+* GDB now supports the vector ABI on S/390 GNU/Linux targets.
+
 * Python Scripting
 
   ** gdb.Objfile objects have a new attribute "username",
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index bb9b28e..edc0da1 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -54,6 +54,8 @@ 
 #include "cli/cli-utils.h"
 #include <ctype.h>
 #include "elf/common.h"
+#include "elf/s390.h"
+#include "elf-bfd.h"
 
 #include "features/s390-linux32.c"
 #include "features/s390-linux32v1.c"
@@ -80,6 +82,12 @@  enum s390_abi_kind
   ABI_LINUX_ZSERIES
 };
 
+enum s390_vector_abi_kind
+{
+  S390_VECTOR_ABI_NONE,
+  S390_VECTOR_ABI_128
+};
+
 /* The tdep structure.  */
 
 struct gdbarch_tdep
@@ -87,6 +95,9 @@  struct gdbarch_tdep
   /* ABI version.  */
   enum s390_abi_kind abi;
 
+  /* Vector ABI.  */
+  enum s390_vector_abi_kind vector_abi;
+
   /* Pseudo register numbers.  */
   int gpr_full_regnum;
   int pc_regnum;
@@ -2395,14 +2406,24 @@  s390_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
      float x;
      struct { float x };
      struct { struct { float x; } x; };
-     struct { struct { struct { float x; } x; } x; };  */
+     struct { struct { struct { float x; } x; } x; };
+
+   However, if an inner type is smaller than MIN_SIZE, abort the
+   unwrapping.  */
 
 static struct type *
-s390_effective_inner_type (struct type *type)
+s390_effective_inner_type (struct type *type, unsigned int min_size)
 {
   while (TYPE_CODE (type) == TYPE_CODE_STRUCT
 	 && TYPE_NFIELDS (type) == 1)
-    type = check_typedef (TYPE_FIELD_TYPE (type, 0));
+    {
+      struct type *inner = check_typedef (TYPE_FIELD_TYPE (type, 0));
+
+      if (TYPE_LENGTH (inner) < min_size)
+	break;
+      type = inner;
+    }
+
   return type;
 }
 
@@ -2419,12 +2440,26 @@  s390_function_arg_float (struct type *type)
 
   /* A struct containing just a float or double is passed like a float
      or double.  */
-  type = s390_effective_inner_type (type);
+  type = s390_effective_inner_type (type, 0);
 
   return (TYPE_CODE (type) == TYPE_CODE_FLT
 	  || TYPE_CODE (type) == TYPE_CODE_DECFLOAT);
 }
 
+/* Return non-zero if TYPE should be passed like a vector.  */
+
+static int
+s390_function_arg_vector (struct type *type)
+{
+  if (TYPE_LENGTH (type) > 16)
+    return 0;
+
+  /* Structs containing just a vector are passed like a vector.  */
+  type = s390_effective_inner_type (type, TYPE_LENGTH (type));
+
+  return TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type);
+}
+
 /* Determine whether N is a power of two.  */
 
 static int
@@ -2434,8 +2469,8 @@  is_power_of_two (unsigned int n)
 }
 
 /* For an argument whose type is TYPE and which is not passed like a
-   float, return non-zero if it should be passed like "int" or "long
-   long".  */
+   float or vector, return non-zero if it should be passed like "int"
+   or "long long".  */
 
 static int
 s390_function_arg_integer (struct type *type)
@@ -2465,9 +2500,9 @@  struct s390_arg_state
   {
     /* Register cache, or NULL, if we are in "preparation mode".  */
     struct regcache *regcache;
-    /* Next available general/floating-point register for argument
-       passing.  */
-    int gr, fr;
+    /* Next available general/floating-point/vector register for
+       argument passing.  */
+    int gr, fr, vr;
     /* Current pointer to copy area (grows downwards).  */
     CORE_ADDR copy;
     /* Current pointer to parameter area (grows upwards).  */
@@ -2482,7 +2517,7 @@  struct s390_arg_state
 static void
 s390_handle_arg (struct s390_arg_state *as, struct value *arg,
 		 struct gdbarch_tdep *tdep, int word_size,
-		 enum bfd_endian byte_order)
+		 enum bfd_endian byte_order, int is_unnamed)
 {
   struct type *type = check_typedef (value_type (arg));
   unsigned int length = TYPE_LENGTH (type);
@@ -2514,6 +2549,28 @@  s390_handle_arg (struct s390_arg_state *as, struct value *arg,
 			  length);
 	}
     }
+  else if (tdep->vector_abi == S390_VECTOR_ABI_128
+	   && s390_function_arg_vector (type))
+    {
+      static const char use_vr[] = {24, 26, 28, 30, 25, 27, 29, 31};
+
+      if (!is_unnamed && as->vr < ARRAY_SIZE (use_vr))
+	{
+	  int regnum = S390_V24_REGNUM + use_vr[as->vr] - 24;
+
+	  if (write_mode)
+	    regcache_cooked_write_part (as->regcache, regnum,
+					0, length,
+					value_contents (arg));
+	  as->vr++;
+	}
+      else
+	{
+	  if (write_mode)
+	    write_memory (as->argp, value_contents (arg), length);
+	  as->argp = align_up (as->argp + length, word_size);
+	}
+    }
   else if (s390_function_arg_integer (type) && length <= word_size)
     {
       ULONGEST val;
@@ -2626,10 +2683,15 @@  s390_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
   int i;
   struct s390_arg_state arg_state, arg_prep;
   CORE_ADDR param_area_start, new_sp;
+  struct type *ftype = check_typedef (value_type (function));
+
+  if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
+    ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
 
   arg_prep.copy = sp;
   arg_prep.gr = struct_return ? 3 : 2;
   arg_prep.fr = 0;
+  arg_prep.vr = 0;
   arg_prep.argp = 0;
   arg_prep.regcache = NULL;
 
@@ -2639,7 +2701,8 @@  s390_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
   /* Update arg_state.copy with the start of the reference-to-copy area
      and arg_state.argp with the size of the parameter area.  */
   for (i = 0; i < nargs; i++)
-    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order);
+    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
+		     TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
 
   param_area_start = align_down (arg_state.copy - arg_state.argp, 8);
 
@@ -2665,7 +2728,8 @@  s390_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
 
   /* Write all parameters.  */
   for (i = 0; i < nargs; i++)
-    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order);
+    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
+		     TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
 
   /* Store return PSWA.  In 31-bit mode, keep addressing mode bit.  */
   if (word_size == 4)
@@ -2731,6 +2795,16 @@  s390_register_return_value (struct gdbarch *gdbarch, struct type *type,
 	regcache_cooked_read_part (regcache, S390_F0_REGNUM,
 				   0, length, out);
     }
+  else if (code == TYPE_CODE_ARRAY)
+    {
+      /* Vector: left-aligned in v24.  */
+      if (in != NULL)
+	regcache_cooked_write_part (regcache, S390_V24_REGNUM,
+				    0, length, in);
+      else
+	regcache_cooked_read_part (regcache, S390_V24_REGNUM,
+				   0, length, out);
+    }
   else if (length <= word_size)
     {
       /* Integer: zero- or sign-extended in r2.  */
@@ -2782,10 +2856,15 @@  s390_return_value (struct gdbarch *gdbarch, struct value *function,
     {
     case TYPE_CODE_STRUCT:
     case TYPE_CODE_UNION:
-    case TYPE_CODE_ARRAY:
     case TYPE_CODE_COMPLEX:
       rvc = RETURN_VALUE_STRUCT_CONVENTION;
       break;
+    case TYPE_CODE_ARRAY:
+      rvc = (gdbarch_tdep (gdbarch)->vector_abi == S390_VECTOR_ABI_128
+	     && TYPE_LENGTH (type) <= 16 && TYPE_VECTOR (type))
+	? RETURN_VALUE_REGISTER_CONVENTION
+	: RETURN_VALUE_STRUCT_CONVENTION;
+      break;
     default:
       rvc = TYPE_LENGTH (type) <= 8
 	? RETURN_VALUE_REGISTER_CONVENTION
@@ -2901,6 +2980,7 @@  s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   struct gdbarch *gdbarch;
   struct gdbarch_tdep *tdep;
   int tdep_abi;
+  enum s390_vector_abi_kind vector_abi;
   int have_upper = 0;
   int have_linux_v1 = 0;
   int have_linux_v2 = 0;
@@ -3083,6 +3163,18 @@  s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	}
     }
 
+  /* Determine vector ABI.  */
+  vector_abi = S390_VECTOR_ABI_NONE;
+#ifdef HAVE_ELF
+  if (have_vx
+      && info.abfd != NULL
+      && info.abfd->format == bfd_object
+      && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour
+      && bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU,
+				   Tag_GNU_S390_ABI_Vector) == 2)
+    vector_abi = S390_VECTOR_ABI_128;
+#endif
+
   /* Find a candidate among extant architectures.  */
   for (arches = gdbarch_list_lookup_by_info (arches, &info);
        arches != NULL;
@@ -3093,6 +3185,8 @@  s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	continue;
       if (tdep->abi != tdep_abi)
 	continue;
+      if (tdep->vector_abi != vector_abi)
+	continue;
       if ((tdep->gpr_full_regnum != -1) != have_upper)
 	continue;
       if (tdesc_data != NULL)
@@ -3103,6 +3197,7 @@  s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   /* Otherwise create a new gdbarch for the specified machine type.  */
   tdep = XCNEW (struct gdbarch_tdep);
   tdep->abi = tdep_abi;
+  tdep->vector_abi = vector_abi;
   tdep->have_linux_v1 = have_linux_v1;
   tdep->have_linux_v2 = have_linux_v2;
   tdep->have_tdb = have_tdb;