• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
skip_until(Dwarf_Die * child,int tag)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
dwarf_bytesize_aux(Dwarf_Die * die,Dwarf_Word * sizep)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
member_is_fp(Dwarf_Die * membdie,Dwarf_Word * sizep,Dwarf_Word * countp)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
hfa_type(Dwarf_Die * ftypedie,int tag,Dwarf_Word * sizep,Dwarf_Word * countp)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
pass_in_gpr(const Dwarf_Op ** locp,Dwarf_Word size)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
pass_by_ref(const Dwarf_Op ** locp)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
pass_hfa(const Dwarf_Op ** locp,Dwarf_Word size,Dwarf_Word count)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
pass_in_simd(const Dwarf_Op ** locp)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
aarch64_return_value_location(Dwarf_Die * functypedie,const Dwarf_Op ** locp)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