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