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
42static int
43skip_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
54static int
55dwarf_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.  */
77static 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.  */
83static int
84member_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
126static int
127hfa_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
192static int
193pass_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
205static int
206pass_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
214static int
215pass_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
259static int
260pass_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
267int
268aarch64_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