LCOV - code coverage report
Current view: top level - backends - aarch64_retval.c (source / functions) Hit Total Coverage
Test: elfutils-0.178 Lines: 118 132 89.4 %
Date: 2019-11-26 23:55:16 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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         206 : skip_until (Dwarf_Die *child, int tag)
      44             : {
      45         206 :   int i;
      46         206 :   while (DWARF_TAG_OR_RETURN (child) != tag)
      47           0 :     if ((i = dwarf_siblingof (child, child)) != 0)
      48             :       /* If there are no members, then this is not a HFA.  Errors
      49             :          are propagated.  */
      50           0 :       return i;
      51             :   return 0;
      52             : }
      53             : 
      54             : static int
      55          86 : dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep)
      56             : {
      57          86 :   int bits;
      58          86 :   if (((bits = 8 * dwarf_bytesize (die)) < 0
      59           0 :        && (bits = dwarf_bitsize (die)) < 0)
      60          86 :       || bits % 8 != 0)
      61           0 :     return -1;
      62             : 
      63          86 :   *sizep = bits / 8;
      64          86 :   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         222 : member_is_fp (Dwarf_Die *membdie, Dwarf_Word *sizep, Dwarf_Word *countp)
      85             : {
      86         222 :   Dwarf_Die typedie;
      87         222 :   int tag = dwarf_peeled_die_type (membdie, &typedie);
      88         222 :   switch (tag)
      89             :     {
      90          74 :     case DW_TAG_base_type:;
      91          74 :       Dwarf_Word encoding;
      92          74 :       Dwarf_Attribute attr_mem;
      93          74 :       if (dwarf_attr_integrate (&typedie, DW_AT_encoding, &attr_mem) == NULL
      94          74 :           || dwarf_formudata (&attr_mem, &encoding) != 0)
      95           0 :         return -1;
      96             : 
      97          74 :       switch (encoding)
      98             :         {
      99           6 :         case DW_ATE_complex_float:
     100           6 :           *countp = 2;
     101           6 :           break;
     102             : 
     103          66 :         case DW_ATE_float:
     104          66 :           *countp = 1;
     105          66 :           break;
     106             : 
     107             :         default:
     108             :           return 1;
     109             :         }
     110             : 
     111          72 :       if (dwarf_bytesize_aux (&typedie, sizep) < 0)
     112             :         return -1;
     113             : 
     114          72 :       *sizep /= *countp;
     115          72 :       return 0;
     116             : 
     117         148 :     case DW_TAG_structure_type:
     118             :     case DW_TAG_union_type:
     119             :     case DW_TAG_array_type:
     120         148 :       return hfa_type (&typedie, tag, sizep, countp);
     121             :     }
     122             : 
     123             :   return 1;
     124             : }
     125             : 
     126             : static int
     127         251 : hfa_type (Dwarf_Die *ftypedie, int tag, Dwarf_Word *sizep, Dwarf_Word *countp)
     128             : {
     129         251 :   assert (tag == DW_TAG_structure_type || tag == DW_TAG_class_type
     130             :           || tag == DW_TAG_union_type || tag == DW_TAG_array_type);
     131             : 
     132         251 :   int i;
     133         251 :   if (tag == DW_TAG_array_type)
     134             :     {
     135         160 :       Dwarf_Word tot_size;
     136         160 :       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         160 :       bool vec;
     142         160 :       Dwarf_Attribute attr_mem;
     143         160 :       if (dwarf_formflag (dwarf_attr_integrate (ftypedie, DW_AT_GNU_vector,
     144             :                                                 &attr_mem), &vec) == 0
     145         144 :           && vec)
     146             :         {
     147         144 :           *sizep = tot_size;
     148         144 :           *countp = 1;
     149             : 
     150         144 :           return 0;
     151             :         }
     152             : 
     153          16 :       if ((i = member_is_fp (ftypedie, sizep, countp)) == 0)
     154             :         {
     155          15 :           *countp = tot_size / *sizep;
     156          15 :           return 0;
     157             :         }
     158             : 
     159             :       return i;
     160             :     }
     161             : 
     162             :   /* Find first DW_TAG_member and determine its type.  */
     163          91 :   Dwarf_Die member;
     164          91 :   if ((i = dwarf_child (ftypedie, &member) != 0))
     165             :     return i;
     166             : 
     167          91 :   if ((i = skip_until (&member, DW_TAG_member)) != 0)
     168             :     return i;
     169             : 
     170          91 :   *countp = 0;
     171          91 :   if ((i = member_is_fp (&member, sizep, countp)) != 0)
     172             :     return i;
     173             : 
     174         204 :   while ((i = dwarf_siblingof (&member, &member)) == 0
     175         115 :          && (i = skip_until (&member, DW_TAG_member)) == 0)
     176             :     {
     177         115 :       Dwarf_Word size, count;
     178         115 :       if ((i = member_is_fp (&member, &size, &count)) != 0)
     179           0 :         return i;
     180             : 
     181         115 :       if (*sizep != size)
     182             :         return 1;
     183             : 
     184         115 :       *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          89 :   return i < 0 ? i : 0;
     190             : }
     191             : 
     192             : static int
     193             : pass_in_gpr (const Dwarf_Op **locp, Dwarf_Word size)
     194             : {
     195           8 :   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           8 :   *locp = loc;
     202           8 :   return size <= 8 ? 1 : 4;
     203             : }
     204             : 
     205             : static int
     206             : pass_by_ref (const Dwarf_Op **locp)
     207             : {
     208           8 :   static const Dwarf_Op loc[] = { { .atom = DW_OP_breg0 } };
     209             : 
     210           8 :   *locp = loc;
     211           8 :   return 1;
     212             : }
     213             : 
     214             : static int
     215         101 : pass_hfa (const Dwarf_Op **locp, Dwarf_Word size, Dwarf_Word count)
     216             : {
     217         101 :   assert (count >= 1 && count <= 4);
     218         101 :   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         101 :   switch (size)
     233             :     {
     234           0 :     case 2:;
     235           0 :       DEFINE_FPREG (loc_hfa_2, 2);
     236           0 :       *locp = loc_hfa_2;
     237           0 :       break;
     238             : 
     239          11 :     case 4:;
     240          11 :       DEFINE_FPREG (loc_hfa_4, 4);
     241          11 :       *locp = loc_hfa_4;
     242          11 :       break;
     243             : 
     244          44 :     case 8:;
     245          44 :       DEFINE_FPREG (loc_hfa_8, 8);
     246          44 :       *locp = loc_hfa_8;
     247          44 :       break;
     248             : 
     249          46 :     case 16:;
     250          46 :       DEFINE_FPREG (loc_hfa_16, 16);
     251          46 :       *locp = loc_hfa_16;
     252          46 :       break;
     253             :     }
     254             : #undef DEFINE_FPREG
     255             : 
     256         101 :   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           3 :   return pass_hfa (locp, 8, 1);
     265             : }
     266             : 
     267             : int
     268         117 : 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         117 :   Dwarf_Die typedie;
     273         117 :   int tag = dwarf_peeled_die_type (functypedie, &typedie);
     274         117 :   if (tag <= 0)
     275             :     return tag;
     276             : 
     277         117 :   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         117 :   if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type
     283          26 :       || tag == DW_TAG_class_type || tag == DW_TAG_array_type)
     284             :     {
     285         103 :       Dwarf_Word base_size, count;
     286         103 :       switch (hfa_type (&typedie, tag, &base_size, &count))
     287             :         {
     288             :         default:
     289         103 :           return -1;
     290             : 
     291         101 :         case 0:
     292         101 :           assert (count > 0);
     293         101 :           if (count <= 4)
     294          95 :             return pass_hfa (locp, base_size, count);
     295           8 :           FALLTHROUGH;
     296             : 
     297             :         case 1:
     298             :           /* Not a HFA.  */
     299           8 :           if (dwarf_aggregate_size (&typedie, &size) < 0)
     300             :             return -1;
     301           8 :           if (size > 16)
     302           8 :             return pass_by_ref (locp);
     303             :         }
     304             :     }
     305             : 
     306          28 :   if (tag == DW_TAG_base_type
     307          14 :       || tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
     308             :     {
     309          14 :       if (dwarf_bytesize_aux (&typedie, &size) < 0)
     310             :         {
     311           0 :           if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
     312           0 :             size = 8;
     313             :           else
     314             :             return -1;
     315             :         }
     316             : 
     317          14 :       Dwarf_Attribute attr_mem;
     318          14 :       if (tag == DW_TAG_base_type)
     319             :         {
     320          13 :           Dwarf_Word encoding;
     321          13 :           if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
     322             :                                                      &attr_mem),
     323             :                                &encoding) != 0)
     324             :             return -1;
     325             : 
     326          13 :           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           3 :             case DW_ATE_float:
     332           3 :               switch (size)
     333             :                 {
     334             :                 case 2: /* half */
     335             :                 case 4: /* sigle */
     336             :                 case 8: /* double */
     337             :                 case 16: /* quad */
     338           3 :                   return pass_in_simd (locp);
     339             : 
     340             :                 default:
     341             :                   return -2;
     342             :                 }
     343             : 
     344           3 :             case DW_ATE_complex_float:
     345           3 :               switch (size)
     346             :                 {
     347           3 :                 case 8: /* float _Complex */
     348             :                 case 16: /* double _Complex */
     349             :                 case 32: /* long double _Complex */
     350           3 :                   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           7 :             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           8 :               return pass_in_gpr (locp, size);
     366             :             }
     367             : 
     368             :           return -2;
     369             :         }
     370             :       else
     371           1 :         return pass_in_gpr (locp, size);
     372             :     }
     373             : 
     374           0 :   *locp = NULL;
     375           0 :   return 0;
     376             : }

Generated by: LCOV version 1.13