2006-01-12  Roland McGrath  <roland@redhat.com>

	* s390_retval.c: New file.
	* Makefile.am (s390_SRCS): Add it.
	* s390_init.c (s390_init): Install return_value_location hook.


diff --git a/backends/ChangeLog b/backends/ChangeLog
index c7f82a9..84515e5 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,9 @@
+2006-01-12  Roland McGrath  <roland@redhat.com>
+
+	* s390_retval.c: New file.
+	* Makefile.am (s390_SRCS): Add it.
+	* s390_init.c (s390_init): Install return_value_location hook.
+
 2006-01-11  Roland McGrath  <roland@redhat.com>
 
 	* s390_regs.c: New file.
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 7719b6f..d85d52f 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -96,7 +96,7 @@
 libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS)
 am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os)
 
-s390_SRCS = s390_init.c s390_symbol.c s390_regs.c
+s390_SRCS = s390_init.c s390_symbol.c s390_regs.c s390_retval.c
 libebl_s390_pic_a_SOURCES = $(s390_SRCS)
 am_libebl_s390_pic_a_OBJECTS = $(s390_SRCS:.c=.os)
 
diff --git a/backends/s390_init.c b/backends/s390_init.c
index d39495c..c956e82 100644
--- a/backends/s390_init.c
+++ b/backends/s390_init.c
@@ -39,6 +39,7 @@
   s390_init_reloc (eh);
   HOOK (eh, reloc_simple_type);
   HOOK (eh, register_name);
+  HOOK (eh, return_value_location);
 
   return MODVERSION;
 }
diff --git a/backends/s390_retval.c b/backends/s390_retval.c
new file mode 100644
index 0000000..ca4da9c
--- /dev/null
+++ b/backends/s390_retval.c
@@ -0,0 +1,142 @@
+/* Function return value location for S/390 ABI.
+   Copyright (C) 2006 Red Hat, Inc.
+
+   This program is Open Source software; you can redistribute it and/or
+   modify it under the terms of the Open Software License version 1.0 as
+   published by the Open Source Initiative.
+
+   You should have received a copy of the Open Software License along
+   with this program; if not, you may obtain a copy of the Open Software
+   License version 1.0 from http://www.opensource.org/licenses/osl.php or
+   by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+   3001 King Ranch Road, Ukiah, CA 95482.   */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <dwarf.h>
+
+#define BACKEND s390_
+#include "libebl_CPU.h"
+
+
+/* %r2, or pair %r2, %r3.  */
+static const Dwarf_Op loc_intreg[] =
+  {
+    { .atom = DW_OP_reg2 }, { .atom = DW_OP_piece, .number = 4 },
+    { .atom = DW_OP_reg3 }, { .atom = DW_OP_piece, .number = 4 },
+  };
+#define nloc_intreg	1
+#define nloc_intregpair	4
+
+/* %f0.  */
+static const Dwarf_Op loc_fpreg[] =
+  {
+    { .atom = DW_OP_reg16 },
+  };
+#define nloc_fpreg	1
+
+/* The return value is a structure and is actually stored in stack space
+   passed in a hidden argument by the caller.  But, the compiler
+   helpfully returns the address of that space in %r2.  */
+static const Dwarf_Op loc_aggregate[] =
+  {
+    { .atom = DW_OP_breg2, .number = 0 }
+  };
+#define nloc_aggregate 1
+
+
+int
+s390_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
+{
+  /* Start with the function's type, and get the DW_AT_type attribute,
+     which is the type of the return value.  */
+
+  Dwarf_Attribute attr_mem;
+  Dwarf_Attribute *attr = dwarf_attr (functypedie, DW_AT_type, &attr_mem);
+  if (attr == NULL)
+    /* The function has no return value, like a `void' function in C.  */
+    return 0;
+
+  Dwarf_Die die_mem;
+  Dwarf_Die *typedie = dwarf_formref_die (attr, &die_mem);
+  int tag = dwarf_tag (typedie);
+
+  /* Follow typedefs and qualifiers to get to the actual type.  */
+  while (tag == DW_TAG_typedef
+	 || tag == DW_TAG_const_type || tag == DW_TAG_volatile_type
+	 || tag == DW_TAG_restrict_type || tag == DW_TAG_mutable_type)
+    {
+      attr = dwarf_attr (typedie, DW_AT_type, &attr_mem);
+      typedie = dwarf_formref_die (attr, &die_mem);
+      tag = dwarf_tag (typedie);
+    }
+
+  Dwarf_Word size;
+  switch (tag)
+    {
+    case -1:
+      return -1;
+
+    case DW_TAG_subrange_type:
+      if (! dwarf_hasattr (typedie, DW_AT_byte_size))
+	{
+	  attr = dwarf_attr (typedie, DW_AT_type, &attr_mem);
+	  typedie = dwarf_formref_die (attr, &die_mem);
+	  tag = dwarf_tag (typedie);
+	}
+      /* Fall through.  */
+
+    case DW_TAG_base_type:
+    case DW_TAG_enumeration_type:
+    case DW_TAG_pointer_type:
+    case DW_TAG_ptr_to_member_type:
+      {
+	Dwarf_Die cudie;
+	uint8_t asize;
+	if (dwarf_diecu (typedie, &cudie, &asize, NULL) == NULL)
+	  return -1;
+
+	if (dwarf_formudata (dwarf_attr (typedie, DW_AT_byte_size,
+					 &attr_mem), &size) != 0)
+	  {
+	    if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
+	      size = asize;
+	    else
+	      return -1;
+	  }
+	if (tag == DW_TAG_base_type)
+	  {
+	    Dwarf_Word encoding;
+	    if (dwarf_formudata (dwarf_attr (typedie, DW_AT_encoding,
+					     &attr_mem), &encoding) != 0)
+	      return -1;
+	    if (encoding == DW_ATE_float && size <= 8)
+	      {
+		*locp = loc_fpreg;
+		return nloc_fpreg;
+	      }
+	  }
+	if (size <= 8)
+	  {
+	    *locp = loc_intreg;
+	    return size <= asize ? nloc_intreg : nloc_intregpair;
+	  }
+      }
+      /* Fall through.  */
+
+    case DW_TAG_structure_type:
+    case DW_TAG_class_type:
+    case DW_TAG_union_type:
+    case DW_TAG_array_type:
+      *locp = loc_aggregate;
+      return nloc_aggregate;
+    }
+
+  /* XXX We don't have a good way to return specific errors from ebl calls.
+     This value means we do not understand the type, but it is well-formed
+     DWARF and might be valid.  */
+  return -2;
+}
diff --git a/config/elfutils.spec.in b/config/elfutils.spec.in
index 2c8245e..70bdd79 100644
--- a/config/elfutils.spec.in
+++ b/config/elfutils.spec.in
@@ -185,6 +185,11 @@
 %{_libdir}/libelf.so
 
 %changelog
+* Thu Jan 12 2006 Roland McGrath <roland@redhat.com> 0.118-1
+- elflint: more tests.
+- libdwfl: New function dwfl_module_register_names.
+- libebl: New backend hook for register names.
+
 * Tue Dec  6 2005 Ulrich Drepper <drepper@redhat.com> 0.118-1
 - elflint: more tests.
 - libdwfl: New function dwfl_module_register_names.