Blame backends/aarch64_retval.c

Packit 032894
/* Function return value location for Linux/AArch64 ABI.
Packit 032894
   Copyright (C) 2013 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of either
Packit 032894
Packit 032894
     * the GNU Lesser General Public License as published by the Free
Packit 032894
       Software Foundation; either version 3 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or
Packit 032894
Packit 032894
     * the GNU General Public License as published by the Free
Packit 032894
       Software Foundation; either version 2 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or both in parallel, as here.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 032894
   General Public License for more details.
Packit 032894
Packit 032894
   You should have received copies of the GNU General Public License and
Packit 032894
   the GNU Lesser General Public License along with this program.  If
Packit 032894
   not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include <stdio.h>
Packit 032894
#include <inttypes.h>
Packit 032894
Packit 032894
#include <assert.h>
Packit 032894
#include <dwarf.h>
Packit 032894
Packit 032894
#define BACKEND aarch64_
Packit 032894
#include "libebl_CPU.h"
Packit 032894
Packit 032894
static int
Packit 032894
skip_until (Dwarf_Die *child, int tag)
Packit 032894
{
Packit 032894
  int i;
Packit 032894
  while (DWARF_TAG_OR_RETURN (child) != tag)
Packit 032894
    if ((i = dwarf_siblingof (child, child)) != 0)
Packit 032894
      /* If there are no members, then this is not a HFA.  Errors
Packit 032894
	 are propagated.  */
Packit 032894
      return i;
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep)
Packit 032894
{
Packit 032894
  int bits;
Packit 032894
  if (((bits = 8 * dwarf_bytesize (die)) < 0
Packit 032894
       && (bits = dwarf_bitsize (die)) < 0)
Packit 032894
      || bits % 8 != 0)
Packit 032894
    return -1;
Packit 032894
Packit 032894
  *sizep = bits / 8;
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
/* HFA (Homogeneous Floating-point Aggregate) is an aggregate type
Packit 032894
   whose members are all of the same floating-point type, which is
Packit 032894
   then base type of this HFA.  Instead of being floating-point types
Packit 032894
   directly, members can instead themselves be HFA.  Such HFA fields
Packit 032894
   are handled as if their type were HFA base type.
Packit 032894
Packit 032894
   This function returns 0 if TYPEDIE is HFA, 1 if it is not, or -1 if
Packit 032894
   there were errors.  In the former case, *SIZEP contains byte size
Packit 032894
   of the base type (e.g. 8 for IEEE double).  *COUNT is set to the
Packit 032894
   number of leaf members of the HFA.  */
Packit 032894
static int hfa_type (Dwarf_Die *ftypedie, int tag,
Packit 032894
		     Dwarf_Word *sizep, Dwarf_Word *countp);
Packit 032894
Packit 032894
/* Return 0 if MEMBDIE refers to a member with a floating-point or HFA
Packit 032894
   type, or 1 if it's not.  Return -1 for errors.  The meaning of the
Packit 032894
   remaining arguments is as documented at hfa_type.  */
Packit 032894
static int
Packit 032894
member_is_fp (Dwarf_Die *membdie, Dwarf_Word *sizep, Dwarf_Word *countp)
Packit 032894
{
Packit 032894
  Dwarf_Die typedie;
Packit 032894
  int tag = dwarf_peeled_die_type (membdie, &typedie);
Packit 032894
  switch (tag)
Packit 032894
    {
Packit 032894
    case DW_TAG_base_type:;
Packit 032894
      Dwarf_Word encoding;
Packit 032894
      Dwarf_Attribute attr_mem;
Packit 032894
      if (dwarf_attr_integrate (&typedie, DW_AT_encoding, &attr_mem) == NULL
Packit 032894
	  || dwarf_formudata (&attr_mem, &encoding) != 0)
Packit 032894
	return -1;
Packit 032894
Packit 032894
      switch (encoding)
Packit 032894
	{
Packit 032894
	case DW_ATE_complex_float:
Packit 032894
	  *countp = 2;
Packit 032894
	  break;
Packit 032894
Packit 032894
	case DW_ATE_float:
Packit 032894
	  *countp = 1;
Packit 032894
	  break;
Packit 032894
Packit 032894
	default:
Packit 032894
	  return 1;
Packit 032894
	}
Packit 032894
Packit 032894
      if (dwarf_bytesize_aux (&typedie, sizep) < 0)
Packit 032894
	return -1;
Packit 032894
Packit 032894
      *sizep /= *countp;
Packit 032894
      return 0;
Packit 032894
Packit 032894
    case DW_TAG_structure_type:
Packit 032894
    case DW_TAG_union_type:
Packit 032894
    case DW_TAG_array_type:
Packit 032894
      return hfa_type (&typedie, tag, sizep, countp);
Packit 032894
    }
Packit 032894
Packit 032894
  return 1;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
hfa_type (Dwarf_Die *ftypedie, int tag, Dwarf_Word *sizep, Dwarf_Word *countp)
Packit 032894
{
Packit 032894
  assert (tag == DW_TAG_structure_type || tag == DW_TAG_class_type
Packit 032894
	  || tag == DW_TAG_union_type || tag == DW_TAG_array_type);
Packit 032894
Packit 032894
  int i;
Packit 032894
  if (tag == DW_TAG_array_type)
Packit 032894
    {
Packit 032894
      Dwarf_Word tot_size;
Packit 032894
      if (dwarf_aggregate_size (ftypedie, &tot_size) < 0)
Packit 032894
	return -1;
Packit 032894
Packit 032894
      /* For vector types, we don't care about the underlying
Packit 032894
	 type, but only about the vector type itself.  */
Packit 032894
      bool vec;
Packit 032894
      Dwarf_Attribute attr_mem;
Packit 032894
      if (dwarf_formflag (dwarf_attr_integrate (ftypedie, DW_AT_GNU_vector,
Packit 032894
						&attr_mem), &vec) == 0
Packit 032894
	  && vec)
Packit 032894
	{
Packit 032894
	  *sizep = tot_size;
Packit 032894
	  *countp = 1;
Packit 032894
Packit 032894
	  return 0;
Packit 032894
	}
Packit 032894
Packit 032894
      if ((i = member_is_fp (ftypedie, sizep, countp)) == 0)
Packit 032894
	{
Packit 032894
	  *countp = tot_size / *sizep;
Packit 032894
	  return 0;
Packit 032894
	}
Packit 032894
Packit 032894
      return i;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Find first DW_TAG_member and determine its type.  */
Packit 032894
  Dwarf_Die member;
Packit 032894
  if ((i = dwarf_child (ftypedie, &member) != 0))
Packit 032894
    return i;
Packit 032894
Packit 032894
  if ((i = skip_until (&member, DW_TAG_member)) != 0)
Packit 032894
    return i;
Packit 032894
Packit 032894
  *countp = 0;
Packit 032894
  if ((i = member_is_fp (&member, sizep, countp)) != 0)
Packit 032894
    return i;
Packit 032894
Packit 032894
  while ((i = dwarf_siblingof (&member, &member)) == 0
Packit 032894
	 && (i = skip_until (&member, DW_TAG_member)) == 0)
Packit 032894
    {
Packit 032894
      Dwarf_Word size, count;
Packit 032894
      if ((i = member_is_fp (&member, &size, &count)) != 0)
Packit 032894
	return i;
Packit 032894
Packit 032894
      if (*sizep != size)
Packit 032894
	return 1;
Packit 032894
Packit 032894
      *countp += count;
Packit 032894
    }
Packit 032894
Packit 032894
  /* At this point we already have at least one FP member, which means
Packit 032894
     FTYPEDIE is an HFA.  So either return 0, or propagate error.  */
Packit 032894
  return i < 0 ? i : 0;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
pass_in_gpr (const Dwarf_Op **locp, Dwarf_Word size)
Packit 032894
{
Packit 032894
  static const Dwarf_Op loc[] =
Packit 032894
    {
Packit 032894
      { .atom = DW_OP_reg0 }, { .atom = DW_OP_piece, .number = 8 },
Packit 032894
      { .atom = DW_OP_reg1 }, { .atom = DW_OP_piece, .number = 8 }
Packit 032894
    };
Packit 032894
Packit 032894
  *locp = loc;
Packit 032894
  return size <= 8 ? 1 : 4;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
pass_by_ref (const Dwarf_Op **locp)
Packit 032894
{
Packit 032894
  static const Dwarf_Op loc[] = { { .atom = DW_OP_breg0 } };
Packit 032894
Packit 032894
  *locp = loc;
Packit 032894
  return 1;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
pass_hfa (const Dwarf_Op **locp, Dwarf_Word size, Dwarf_Word count)
Packit 032894
{
Packit 032894
  assert (count >= 1 && count <= 4);
Packit 032894
  assert (size == 2 || size == 4 || size == 8 || size == 16);
Packit 032894
Packit 032894
#define DEFINE_FPREG(NAME, SIZE)		\
Packit 032894
  static const Dwarf_Op NAME[] = {		\
Packit 032894
    { .atom = DW_OP_regx, .number = 64 },	\
Packit 032894
    { .atom = DW_OP_piece, .number = SIZE },	\
Packit 032894
    { .atom = DW_OP_regx, .number = 65 },	\
Packit 032894
    { .atom = DW_OP_piece, .number = SIZE },	\
Packit 032894
    { .atom = DW_OP_regx, .number = 66 },	\
Packit 032894
    { .atom = DW_OP_piece, .number = SIZE },	\
Packit 032894
    { .atom = DW_OP_regx, .number = 67 },	\
Packit 032894
    { .atom = DW_OP_piece, .number = SIZE }	\
Packit 032894
  }
Packit 032894
Packit 032894
  switch (size)
Packit 032894
    {
Packit 032894
    case 2:;
Packit 032894
      DEFINE_FPREG (loc_hfa_2, 2);
Packit 032894
      *locp = loc_hfa_2;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 4:;
Packit 032894
      DEFINE_FPREG (loc_hfa_4, 4);
Packit 032894
      *locp = loc_hfa_4;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 8:;
Packit 032894
      DEFINE_FPREG (loc_hfa_8, 8);
Packit 032894
      *locp = loc_hfa_8;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 16:;
Packit 032894
      DEFINE_FPREG (loc_hfa_16, 16);
Packit 032894
      *locp = loc_hfa_16;
Packit 032894
      break;
Packit 032894
    }
Packit 032894
#undef DEFINE_FPREG
Packit 032894
Packit 032894
  return count == 1 ? 1 : 2 * count;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
pass_in_simd (const Dwarf_Op **locp)
Packit 032894
{
Packit 032894
  /* This is like passing single-element HFA.  Size doesn't matter, so
Packit 032894
     pretend it's for example double.  */
Packit 032894
  return pass_hfa (locp, 8, 1);
Packit 032894
}
Packit 032894
Packit 032894
int
Packit 032894
aarch64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
Packit 032894
{
Packit 032894
  /* Start with the function's type, and get the DW_AT_type attribute,
Packit 032894
     which is the type of the return value.  */
Packit 032894
  Dwarf_Die typedie;
Packit 032894
  int tag = dwarf_peeled_die_type (functypedie, &typedie);
Packit 032894
  if (tag <= 0)
Packit 032894
    return tag;
Packit 032894
Packit 032894
  Dwarf_Word size = (Dwarf_Word)-1;
Packit 032894
Packit 032894
  /* If the argument type is a Composite Type that is larger than 16
Packit 032894
     bytes, then the argument is copied to memory allocated by the
Packit 032894
     caller and the argument is replaced by a pointer to the copy.  */
Packit 032894
  if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type
Packit 032894
      || tag == DW_TAG_class_type || tag == DW_TAG_array_type)
Packit 032894
    {
Packit 032894
      Dwarf_Word base_size, count;
Packit 032894
      switch (hfa_type (&typedie, tag, &base_size, &count))
Packit 032894
	{
Packit 032894
	default:
Packit 032894
	  return -1;
Packit 032894
Packit 032894
	case 0:
Packit 032894
	  assert (count > 0);
Packit 032894
	  if (count <= 4)
Packit 032894
	    return pass_hfa (locp, base_size, count);
Packit 032894
	  FALLTHROUGH;
Packit 032894
Packit 032894
	case 1:
Packit 032894
	  /* Not a HFA.  */
Packit 032894
	  if (dwarf_aggregate_size (&typedie, &size) < 0)
Packit 032894
	    return -1;
Packit 032894
	  if (size > 16)
Packit 032894
	    return pass_by_ref (locp);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (tag == DW_TAG_base_type
Packit 032894
      || tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
Packit 032894
    {
Packit 032894
      if (dwarf_bytesize_aux (&typedie, &size) < 0)
Packit 032894
	{
Packit 032894
	  if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
Packit 032894
	    size = 8;
Packit 032894
	  else
Packit 032894
	    return -1;
Packit 032894
	}
Packit 032894
Packit 032894
      Dwarf_Attribute attr_mem;
Packit 032894
      if (tag == DW_TAG_base_type)
Packit 032894
	{
Packit 032894
	  Dwarf_Word encoding;
Packit 032894
	  if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
Packit 032894
						     &attr_mem),
Packit 032894
			       &encoding) != 0)
Packit 032894
	    return -1;
Packit 032894
Packit 032894
	  switch (encoding)
Packit 032894
	    {
Packit 032894
	      /* If the argument is a Half-, Single-, Double- or Quad-
Packit 032894
		 precision Floating-point [...] the argument is allocated
Packit 032894
		 to the least significant bits of register v[NSRN].  */
Packit 032894
	    case DW_ATE_float:
Packit 032894
	      switch (size)
Packit 032894
		{
Packit 032894
		case 2: /* half */
Packit 032894
		case 4: /* sigle */
Packit 032894
		case 8: /* double */
Packit 032894
		case 16: /* quad */
Packit 032894
		  return pass_in_simd (locp);
Packit 032894
Packit 032894
		default:
Packit 032894
		  return -2;
Packit 032894
		}
Packit 032894
Packit 032894
	    case DW_ATE_complex_float:
Packit 032894
	      switch (size)
Packit 032894
		{
Packit 032894
		case 8: /* float _Complex */
Packit 032894
		case 16: /* double _Complex */
Packit 032894
		case 32: /* long double _Complex */
Packit 032894
		  return pass_hfa (locp, size / 2, 2);
Packit 032894
Packit 032894
		default:
Packit 032894
		  return -2;
Packit 032894
		}
Packit 032894
Packit 032894
	      /* If the argument is an Integral or Pointer Type, the
Packit 032894
		 size of the argument is less than or equal to 8 bytes
Packit 032894
		 [...] the argument is copied to the least significant
Packit 032894
		 bits in x[NGRN].  */
Packit 032894
	    case DW_ATE_boolean:
Packit 032894
	    case DW_ATE_signed:
Packit 032894
	    case DW_ATE_unsigned:
Packit 032894
	    case DW_ATE_unsigned_char:
Packit 032894
	    case DW_ATE_signed_char:
Packit 032894
	      return pass_in_gpr (locp, size);
Packit 032894
	    }
Packit 032894
Packit 032894
	  return -2;
Packit 032894
	}
Packit 032894
      else
Packit 032894
	return pass_in_gpr (locp, size);
Packit 032894
    }
Packit 032894
Packit 032894
  *locp = NULL;
Packit 032894
  return 0;
Packit 032894
}