Home | History | Annotate | Download | only in backends
      1 /* Function return value location for Linux/AArch64 ABI.
      2    Copyright (C) 2013 Red Hat, Inc.
      3    This file is part of elfutils.
      4 
      5    This file is free software; you can redistribute it and/or modify
      6    it under the terms of either
      7 
      8      * the GNU Lesser General Public License as published by the Free
      9        Software Foundation; either version 3 of the License, or (at
     10        your option) any later version
     11 
     12    or
     13 
     14      * the GNU General Public License as published by the Free
     15        Software Foundation; either version 2 of the License, or (at
     16        your option) any later version
     17 
     18    or both in parallel, as here.
     19 
     20    elfutils is distributed in the hope that it will be useful, but
     21    WITHOUT ANY WARRANTY; without even the implied warranty of
     22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     23    General Public License for more details.
     24 
     25    You should have received copies of the GNU General Public License and
     26    the GNU Lesser General Public License along with this program.  If
     27    not, see <http://www.gnu.org/licenses/>.  */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 # include <config.h>
     31 #endif
     32 
     33 #include <stdio.h>
     34 #include <inttypes.h>
     35 
     36 #include <assert.h>
     37 #include <dwarf.h>
     38 
     39 #define BACKEND aarch64_
     40 #include "libebl_CPU.h"
     41 
     42 static int
     43 skip_until (Dwarf_Die *child, int tag)
     44 {
     45   int i;
     46   while (DWARF_TAG_OR_RETURN (child) != tag)
     47     if ((i = dwarf_siblingof (child, child)) != 0)
     48       /* If there are no members, then this is not a HFA.  Errors
     49 	 are propagated.  */
     50       return i;
     51   return 0;
     52 }
     53 
     54 static int
     55 dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep)
     56 {
     57   int bits;
     58   if (((bits = 8 * dwarf_bytesize (die)) < 0
     59        && (bits = dwarf_bitsize (die)) < 0)
     60       || bits % 8 != 0)
     61     return -1;
     62 
     63   *sizep = bits / 8;
     64   return 0;
     65 }
     66 
     67 /* HFA (Homogeneous Floating-point Aggregate) is an aggregate type
     68    whose members are all of the same floating-point type, which is
     69    then base type of this HFA.  Instead of being floating-point types
     70    directly, members can instead themselves be HFA.  Such HFA fields
     71    are handled as if their type were HFA base type.
     72 
     73    This function returns 0 if TYPEDIE is HFA, 1 if it is not, or -1 if
     74    there were errors.  In the former case, *SIZEP contains byte size
     75    of the base type (e.g. 8 for IEEE double).  *COUNT is set to the
     76    number of leaf members of the HFA.  */
     77 static int hfa_type (Dwarf_Die *ftypedie, int tag,
     78 		     Dwarf_Word *sizep, Dwarf_Word *countp);
     79 
     80 /* Return 0 if MEMBDIE refers to a member with a floating-point or HFA
     81    type, or 1 if it's not.  Return -1 for errors.  The meaning of the
     82    remaining arguments is as documented at hfa_type.  */
     83 static int
     84 member_is_fp (Dwarf_Die *membdie, Dwarf_Word *sizep, Dwarf_Word *countp)
     85 {
     86   Dwarf_Die typedie;
     87   int tag = dwarf_peeled_die_type (membdie, &typedie);
     88   switch (tag)
     89     {
     90     case DW_TAG_base_type:;
     91       Dwarf_Word encoding;
     92       Dwarf_Attribute attr_mem;
     93       if (dwarf_attr_integrate (&typedie, DW_AT_encoding, &attr_mem) == NULL
     94 	  || dwarf_formudata (&attr_mem, &encoding) != 0)
     95 	return -1;
     96 
     97       switch (encoding)
     98 	{
     99 	case DW_ATE_complex_float:
    100 	  *countp = 2;
    101 	  break;
    102 
    103 	case DW_ATE_float:
    104 	  *countp = 1;
    105 	  break;
    106 
    107 	default:
    108 	  return 1;
    109 	}
    110 
    111       if (dwarf_bytesize_aux (&typedie, sizep) < 0)
    112 	return -1;
    113 
    114       *sizep /= *countp;
    115       return 0;
    116 
    117     case DW_TAG_structure_type:
    118     case DW_TAG_union_type:
    119     case DW_TAG_array_type:
    120       return hfa_type (&typedie, tag, sizep, countp);
    121     }
    122 
    123   return 1;
    124 }
    125 
    126 static int
    127 hfa_type (Dwarf_Die *ftypedie, int tag, Dwarf_Word *sizep, Dwarf_Word *countp)
    128 {
    129   assert (tag == DW_TAG_structure_type || tag == DW_TAG_class_type
    130 	  || tag == DW_TAG_union_type || tag == DW_TAG_array_type);
    131 
    132   int i;
    133   if (tag == DW_TAG_array_type)
    134     {
    135       Dwarf_Word tot_size;
    136       if (dwarf_aggregate_size (ftypedie, &tot_size) < 0)
    137 	return -1;
    138 
    139       /* For vector types, we don't care about the underlying
    140 	 type, but only about the vector type itself.  */
    141       bool vec;
    142       Dwarf_Attribute attr_mem;
    143       if (dwarf_formflag (dwarf_attr_integrate (ftypedie, DW_AT_GNU_vector,
    144 						&attr_mem), &vec) == 0
    145 	  && vec)
    146 	{
    147 	  *sizep = tot_size;
    148 	  *countp = 1;
    149 
    150 	  return 0;
    151 	}
    152 
    153       if ((i = member_is_fp (ftypedie, sizep, countp)) == 0)
    154 	{
    155 	  *countp = tot_size / *sizep;
    156 	  return 0;
    157 	}
    158 
    159       return i;
    160     }
    161 
    162   /* Find first DW_TAG_member and determine its type.  */
    163   Dwarf_Die member;
    164   if ((i = dwarf_child (ftypedie, &member) != 0))
    165     return i;
    166 
    167   if ((i = skip_until (&member, DW_TAG_member)) != 0)
    168     return i;
    169 
    170   *countp = 0;
    171   if ((i = member_is_fp (&member, sizep, countp)) != 0)
    172     return i;
    173 
    174   while ((i = dwarf_siblingof (&member, &member)) == 0
    175 	 && (i = skip_until (&member, DW_TAG_member)) == 0)
    176     {
    177       Dwarf_Word size, count;
    178       if ((i = member_is_fp (&member, &size, &count)) != 0)
    179 	return i;
    180 
    181       if (*sizep != size)
    182 	return 1;
    183 
    184       *countp += count;
    185     }
    186 
    187   /* At this point we already have at least one FP member, which means
    188      FTYPEDIE is an HFA.  So either return 0, or propagate error.  */
    189   return i < 0 ? i : 0;
    190 }
    191 
    192 static int
    193 pass_in_gpr (const Dwarf_Op **locp, Dwarf_Word size)
    194 {
    195   static const Dwarf_Op loc[] =
    196     {
    197       { .atom = DW_OP_reg0 }, { .atom = DW_OP_piece, .number = 8 },
    198       { .atom = DW_OP_reg1 }, { .atom = DW_OP_piece, .number = 8 }
    199     };
    200 
    201   *locp = loc;
    202   return size <= 8 ? 1 : 4;
    203 }
    204 
    205 static int
    206 pass_by_ref (const Dwarf_Op **locp)
    207 {
    208   static const Dwarf_Op loc[] = { { .atom = DW_OP_breg0 } };
    209 
    210   *locp = loc;
    211   return 1;
    212 }
    213 
    214 static int
    215 pass_hfa (const Dwarf_Op **locp, Dwarf_Word size, Dwarf_Word count)
    216 {
    217   assert (count >= 1 && count <= 4);
    218   assert (size == 2 || size == 4 || size == 8 || size == 16);
    219 
    220 #define DEFINE_FPREG(NAME, SIZE)		\
    221   static const Dwarf_Op NAME[] = {		\
    222     { .atom = DW_OP_regx, .number = 64 },	\
    223     { .atom = DW_OP_piece, .number = SIZE },	\
    224     { .atom = DW_OP_regx, .number = 65 },	\
    225     { .atom = DW_OP_piece, .number = SIZE },	\
    226     { .atom = DW_OP_regx, .number = 66 },	\
    227     { .atom = DW_OP_piece, .number = SIZE },	\
    228     { .atom = DW_OP_regx, .number = 67 },	\
    229     { .atom = DW_OP_piece, .number = SIZE }	\
    230   }
    231 
    232   switch (size)
    233     {
    234     case 2:;
    235       DEFINE_FPREG (loc_hfa_2, 2);
    236       *locp = loc_hfa_2;
    237       break;
    238 
    239     case 4:;
    240       DEFINE_FPREG (loc_hfa_4, 4);
    241       *locp = loc_hfa_4;
    242       break;
    243 
    244     case 8:;
    245       DEFINE_FPREG (loc_hfa_8, 8);
    246       *locp = loc_hfa_8;
    247       break;
    248 
    249     case 16:;
    250       DEFINE_FPREG (loc_hfa_16, 16);
    251       *locp = loc_hfa_16;
    252       break;
    253     }
    254 #undef DEFINE_FPREG
    255 
    256   return count == 1 ? 1 : 2 * count;
    257 }
    258 
    259 static int
    260 pass_in_simd (const Dwarf_Op **locp)
    261 {
    262   /* This is like passing single-element HFA.  Size doesn't matter, so
    263      pretend it's for example double.  */
    264   return pass_hfa (locp, 8, 1);
    265 }
    266 
    267 int
    268 aarch64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
    269 {
    270   /* Start with the function's type, and get the DW_AT_type attribute,
    271      which is the type of the return value.  */
    272   Dwarf_Die typedie;
    273   int tag = dwarf_peeled_die_type (functypedie, &typedie);
    274   if (tag <= 0)
    275     return tag;
    276 
    277   Dwarf_Word size = (Dwarf_Word)-1;
    278 
    279   /* If the argument type is a Composite Type that is larger than 16
    280      bytes, then the argument is copied to memory allocated by the
    281      caller and the argument is replaced by a pointer to the copy.  */
    282   if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type
    283       || tag == DW_TAG_class_type || tag == DW_TAG_array_type)
    284     {
    285       Dwarf_Word base_size, count;
    286       switch (hfa_type (&typedie, tag, &base_size, &count))
    287 	{
    288 	default:
    289 	  return -1;
    290 
    291 	case 0:
    292 	  assert (count > 0);
    293 	  if (count <= 4)
    294 	    return pass_hfa (locp, base_size, count);
    295 	  /* Fall through.  */
    296 
    297 	case 1:
    298 	  /* Not a HFA.  */
    299 	  if (dwarf_aggregate_size (&typedie, &size) < 0)
    300 	    return -1;
    301 	  if (size > 16)
    302 	    return pass_by_ref (locp);
    303 	}
    304     }
    305 
    306   if (tag == DW_TAG_base_type
    307       || tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
    308     {
    309       if (dwarf_bytesize_aux (&typedie, &size) < 0)
    310 	{
    311 	  if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
    312 	    size = 8;
    313 	  else
    314 	    return -1;
    315 	}
    316 
    317       Dwarf_Attribute attr_mem;
    318       if (tag == DW_TAG_base_type)
    319 	{
    320 	  Dwarf_Word encoding;
    321 	  if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
    322 						     &attr_mem),
    323 			       &encoding) != 0)
    324 	    return -1;
    325 
    326 	  switch (encoding)
    327 	    {
    328 	      /* If the argument is a Half-, Single-, Double- or Quad-
    329 		 precision Floating-point [...] the argument is allocated
    330 		 to the least significant bits of register v[NSRN].  */
    331 	    case DW_ATE_float:
    332 	      switch (size)
    333 		{
    334 		case 2: /* half */
    335 		case 4: /* sigle */
    336 		case 8: /* double */
    337 		case 16: /* quad */
    338 		  return pass_in_simd (locp);
    339 
    340 		default:
    341 		  return -2;
    342 		}
    343 
    344 	    case DW_ATE_complex_float:
    345 	      switch (size)
    346 		{
    347 		case 8: /* float _Complex */
    348 		case 16: /* double _Complex */
    349 		case 32: /* long double _Complex */
    350 		  return pass_hfa (locp, size / 2, 2);
    351 
    352 		default:
    353 		  return -2;
    354 		}
    355 
    356 	      /* If the argument is an Integral or Pointer Type, the
    357 		 size of the argument is less than or equal to 8 bytes
    358 		 [...] the argument is copied to the least significant
    359 		 bits in x[NGRN].  */
    360 	    case DW_ATE_boolean:
    361 	    case DW_ATE_signed:
    362 	    case DW_ATE_unsigned:
    363 	    case DW_ATE_unsigned_char:
    364 	    case DW_ATE_signed_char:
    365 	      return pass_in_gpr (locp, size);
    366 	    }
    367 
    368 	  return -2;
    369 	}
    370       else
    371 	return pass_in_gpr (locp, size);
    372     }
    373 
    374   *locp = NULL;
    375   return 0;
    376 }
    377