Branch data Line data Source code
1 : : /* Function return value location for Linux/RISC-V ABI.
2 : : Copyright (C) 2018 Sifive, Inc.
3 : : Copyright (C) 2013 Red Hat, Inc.
4 : : Copyright (C) 2024 Mark J. Wielaard <mark@klomp.org>
5 : : This file is part of elfutils.
6 : :
7 : : This file is free software; you can redistribute it and/or modify
8 : : it under the terms of either
9 : :
10 : : * the GNU Lesser General Public License as published by the Free
11 : : Software Foundation; either version 3 of the License, or (at
12 : : your option) any later version
13 : :
14 : : or
15 : :
16 : : * the GNU General Public License as published by the Free
17 : : Software Foundation; either version 2 of the License, or (at
18 : : your option) any later version
19 : :
20 : : or both in parallel, as here.
21 : :
22 : : elfutils is distributed in the hope that it will be useful, but
23 : : WITHOUT ANY WARRANTY; without even the implied warranty of
24 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 : : General Public License for more details.
26 : :
27 : : You should have received copies of the GNU General Public License and
28 : : the GNU Lesser General Public License along with this program. If
29 : : not, see <http://www.gnu.org/licenses/>. */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : # include <config.h>
33 : : #endif
34 : :
35 : : #include <stdio.h>
36 : : #include <inttypes.h>
37 : :
38 : : #include <assert.h>
39 : : #include <dwarf.h>
40 : :
41 : : #define BACKEND riscv_
42 : : #include "libebl_CPU.h"
43 : :
44 : : static int
45 : 9 : dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep)
46 : : {
47 : 9 : int bits;
48 [ - + ]: 9 : if (((bits = 8 * dwarf_bytesize (die)) < 0
49 [ # # ]: 0 : && (bits = dwarf_bitsize (die)) < 0)
50 [ - + ]: 9 : || bits % 8 != 0)
51 : 0 : return -1;
52 : :
53 : 9 : *sizep = bits / 8;
54 : 9 : return 0;
55 : : }
56 : :
57 : : static int
58 : 3 : pass_in_gpr_lp64 (const Dwarf_Op **locp, Dwarf_Word size)
59 : : {
60 : 3 : static const Dwarf_Op loc[] =
61 : : {
62 : : { .atom = DW_OP_reg10 }, { .atom = DW_OP_piece, .number = 8 },
63 : : { .atom = DW_OP_reg11 }, { .atom = DW_OP_piece, .number = 8 }
64 : : };
65 : :
66 : 3 : *locp = loc;
67 : 3 : return size <= 8 ? 1 : 4;
68 : : }
69 : :
70 : : static int
71 : 0 : pass_by_ref (const Dwarf_Op **locp)
72 : : {
73 : 0 : static const Dwarf_Op loc[] = { { .atom = DW_OP_breg10 } };
74 : :
75 : 0 : *locp = loc;
76 : 0 : return 1;
77 : : }
78 : :
79 : : static int
80 : 0 : pass_in_fpr_lp64f (const Dwarf_Op **locp, Dwarf_Word size)
81 : : {
82 : 0 : static const Dwarf_Op loc[] =
83 : : {
84 : : { .atom = DW_OP_regx, .number = 42 },
85 : : { .atom = DW_OP_piece, .number = 4 },
86 : : { .atom = DW_OP_regx, .number = 43 },
87 : : { .atom = DW_OP_piece, .number = 4 }
88 : : };
89 : :
90 : 0 : *locp = loc;
91 : 0 : return size <= 4 ? 1 : 4;
92 : : }
93 : :
94 : : static int
95 : 2 : pass_in_fpr_lp64d (const Dwarf_Op **locp, Dwarf_Word size)
96 : : {
97 : 2 : static const Dwarf_Op loc[] =
98 : : {
99 : : { .atom = DW_OP_regx, .number = 42 },
100 : : { .atom = DW_OP_piece, .number = 8 },
101 : : { .atom = DW_OP_regx, .number = 43 },
102 : : { .atom = DW_OP_piece, .number = 8 }
103 : : };
104 : :
105 : 2 : *locp = loc;
106 : 2 : return size <= 8 ? 1 : 4;
107 : : }
108 : :
109 : : /* Checks if we can "flatten" the given type, Only handles the simple
110 : : cases where we have a struct with one or two the same base type
111 : : elements. */
112 : : static int
113 : 4 : flatten_aggregate_arg (Dwarf_Die *typedie,
114 : : Dwarf_Word size,
115 : : Dwarf_Die *arg0,
116 : : Dwarf_Die *arg1)
117 : : {
118 : 4 : int tag0, tag1;
119 : 4 : Dwarf_Die member;
120 : 4 : Dwarf_Word encoding0, encoding1;
121 : 4 : Dwarf_Attribute attr;
122 : 4 : Dwarf_Word size0, size1;
123 : :
124 [ - + ]: 4 : if (size < 8 || size > 16)
125 : : return 0;
126 : :
127 [ - + ]: 4 : if (dwarf_child (typedie, arg0) != 0)
128 : : return 0;
129 : :
130 : 4 : tag0 = dwarf_tag (arg0);
131 [ - + ]: 4 : while (tag0 != -1 && tag0 != DW_TAG_member)
132 : : {
133 [ # # ]: 0 : if (dwarf_siblingof (arg0, arg0) != 0)
134 : : return 0;
135 : 0 : tag0 = dwarf_tag (arg0);
136 : : }
137 : :
138 [ - + ]: 4 : if (tag0 != DW_TAG_member)
139 : : return 0;
140 : :
141 : : /* Remember where we are. */
142 : 4 : member = *arg0;
143 : :
144 : 4 : tag0 = dwarf_peeled_die_type (arg0, arg0);
145 [ - + ]: 4 : if (tag0 != DW_TAG_base_type)
146 : : return 0;
147 : :
148 [ + - ]: 4 : if (dwarf_attr_integrate (arg0, DW_AT_encoding, &attr) == NULL
149 [ - + ]: 4 : || dwarf_formudata (&attr, &encoding0) != 0)
150 : 0 : return 0;
151 : :
152 [ - + ]: 4 : if (dwarf_bytesize_aux (arg0, &size0) != 0)
153 : : return 0;
154 : :
155 [ + - ]: 4 : if (size == size0)
156 : : return 1; /* This one member is the whole size. */
157 : :
158 [ - + ]: 4 : if (size != 2 * size0)
159 : : return 0; /* We only handle two of the same. */
160 : :
161 : : /* Look for another member with the same encoding. */
162 [ - + ]: 4 : if (dwarf_siblingof (&member, arg1) != 0)
163 : : return 0;
164 : :
165 : 4 : tag1 = dwarf_tag (arg1);
166 [ - + ]: 4 : while (tag1 != -1 && tag1 != DW_TAG_member)
167 : : {
168 [ # # ]: 0 : if (dwarf_siblingof (arg1, arg1) != 0)
169 : : return 0;
170 : 0 : tag1 = dwarf_tag (arg1);
171 : : }
172 : :
173 [ - + ]: 4 : if (tag1 != DW_TAG_member)
174 : : return 0;
175 : :
176 : 4 : tag1 = dwarf_peeled_die_type (arg1, arg1);
177 [ - + ]: 4 : if (tag1 != DW_TAG_base_type)
178 : : return 0; /* We can only handle two equal base types for now. */
179 : :
180 [ + - ]: 4 : if (dwarf_attr_integrate (arg1, DW_AT_encoding, &attr) == NULL
181 [ + - ]: 4 : || dwarf_formudata (&attr, &encoding1) != 0
182 [ - + ]: 4 : || encoding0 != encoding1)
183 : 0 : return 0; /* We can only handle two of the same for now. */
184 : :
185 [ - + ]: 4 : if (dwarf_bytesize_aux (arg1, &size1) != 0)
186 : : return 0;
187 : :
188 [ - + ]: 4 : if (size0 != size1)
189 : : return 0; /* We can only handle two of the same for now. */
190 : :
191 : : return 1;
192 : : }
193 : :
194 : : /* arg0 and arg1 should be the peeled die types found by
195 : : flatten_aggregate_arg. */
196 : : static int
197 : 4 : pass_by_flattened_arg (const Dwarf_Op **locp,
198 : : Dwarf_Word size,
199 : : Dwarf_Die *arg0,
200 : : Dwarf_Die *arg1 __attribute__((unused)))
201 : : {
202 : : /* For now we just assume arg0 and arg1 are the same type and
203 : : encoding. */
204 : 4 : Dwarf_Word encoding;
205 : 4 : Dwarf_Attribute attr;
206 : :
207 [ + - ]: 4 : if (dwarf_attr_integrate (arg0, DW_AT_encoding, &attr) == NULL
208 [ - + ]: 4 : || dwarf_formudata (&attr, &encoding) != 0)
209 : 0 : return -1;
210 : :
211 [ - + + ]: 4 : switch (encoding)
212 : : {
213 : : case DW_ATE_boolean:
214 : : case DW_ATE_signed:
215 : : case DW_ATE_unsigned:
216 : : case DW_ATE_unsigned_char:
217 : : case DW_ATE_signed_char:
218 [ + + ]: 2 : return pass_in_gpr_lp64 (locp, size);
219 : :
220 : : case DW_ATE_float:
221 [ + + ]: 2 : return pass_in_fpr_lp64d (locp, size);
222 : :
223 : : default:
224 : : return -1;
225 : : }
226 : : }
227 : :
228 : : int
229 : 5 : riscv_return_value_location_lp64ifd (int fp, Dwarf_Die *functypedie,
230 : : const Dwarf_Op **locp)
231 : : {
232 : : /* Start with the function's type, and get the DW_AT_type attribute,
233 : : which is the type of the return value. */
234 : 5 : Dwarf_Die typedie;
235 : 5 : int tag = dwarf_peeled_die_type (functypedie, &typedie);
236 [ + - ]: 5 : if (tag <= 0)
237 : : return tag;
238 : :
239 : 5 : Dwarf_Word size = (Dwarf_Word)-1;
240 : :
241 : : /* If the argument type is a Composite Type that is larger than 16
242 : : bytes, then the argument is copied to memory allocated by the
243 : : caller and the argument is replaced by a pointer to the copy. */
244 [ + + ]: 5 : if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type
245 [ - + ]: 1 : || tag == DW_TAG_class_type || tag == DW_TAG_array_type)
246 : : {
247 : 4 : Dwarf_Die arg0, arg1;
248 : :
249 [ + - ]: 4 : if (dwarf_aggregate_size (&typedie, &size) < 0)
250 : : return -1;
251 : : /* A struct containing just one floating-point real is passed as though
252 : : it were a standalone floating-point real. A struct containing two
253 : : floating-point reals is passed in two floating-point registers, if
254 : : neither is more than FLEN bits wide. A struct containing just one
255 : : complex floating-point number is passed as though it were a struct
256 : : containing two floating-point reals. A struct containing one
257 : : floating-point real and one integer (or bitfield), in either order,
258 : : is passed in a floating-point register and an integer register,
259 : : provided the floating-point real is no more than FLEN bits wide and
260 : : the integer is no more than XLEN bits wide. */
261 [ + - ]: 4 : if (tag == DW_TAG_structure_type
262 [ + - ]: 4 : && flatten_aggregate_arg (&typedie, size, &arg0, &arg1))
263 : 4 : return pass_by_flattened_arg (locp, size, &arg0, &arg1);
264 : : /* Aggregates larger than 2*XLEN bits are passed by reference. */
265 [ # # ]: 0 : else if (size > 16)
266 : 0 : return pass_by_ref (locp);
267 : : /* Aggregates whose total size is no more than XLEN bits are passed in
268 : : a register. Aggregates whose total size is no more than 2*XLEN bits
269 : : are passed in a pair of registers. */
270 : : else
271 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
272 : : }
273 : :
274 [ - + - - ]: 1 : if (tag == DW_TAG_base_type || dwarf_is_pointer (tag))
275 : : {
276 [ - + ]: 1 : if (dwarf_bytesize_aux (&typedie, &size) < 0)
277 : : {
278 [ # # ]: 0 : if (dwarf_is_pointer (tag))
279 : 0 : size = 8;
280 : : else
281 : : return -1;
282 : : }
283 : :
284 : 1 : Dwarf_Attribute attr_mem;
285 [ + - ]: 1 : if (tag == DW_TAG_base_type)
286 : : {
287 : 1 : Dwarf_Word encoding;
288 [ + - ]: 1 : if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
289 : : &attr_mem),
290 : : &encoding) != 0)
291 : : return -1;
292 : :
293 [ + - - - ]: 1 : switch (encoding)
294 : : {
295 : 1 : case DW_ATE_boolean:
296 : : case DW_ATE_signed:
297 : : case DW_ATE_unsigned:
298 : : case DW_ATE_unsigned_char:
299 : : case DW_ATE_signed_char:
300 : : /* Scalars that are at most XLEN bits wide are passed in a single
301 : : argument register. Scalars that are 2*XLEN bits wide are
302 : : passed in a pair of argument registers. Scalars wider than
303 : : 2*XLEN are passed by reference; there are none for LP64D. */
304 [ - + ]: 1 : return pass_in_gpr_lp64 (locp, size);
305 : :
306 : 0 : case DW_ATE_float:
307 : : /* A real floating-point argument is passed in a floating-point
308 : : argument register if it is no more than FLEN bits wide,
309 : : otherwise it is passed according to the integer calling
310 : : convention. */
311 [ # # # # ]: 0 : switch (size)
312 : : {
313 : 0 : case 4: /* single */
314 [ # # # ]: 0 : switch (fp)
315 : : {
316 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
317 : : case EF_RISCV_FLOAT_ABI_SINGLE:
318 : 0 : return pass_in_fpr_lp64d (locp, size);
319 : 0 : case EF_RISCV_FLOAT_ABI_SOFT:
320 : 0 : return pass_in_gpr_lp64 (locp, size);
321 : : default:
322 : : return -2;
323 : : }
324 : 0 : case 8: /* double */
325 [ # # # ]: 0 : switch (fp)
326 : : {
327 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
328 : 0 : return pass_in_fpr_lp64d (locp, size);
329 : 0 : case EF_RISCV_FLOAT_ABI_SINGLE:
330 : : case EF_RISCV_FLOAT_ABI_SOFT:
331 : 0 : return pass_in_gpr_lp64 (locp, size);
332 : : default:
333 : : return -2;
334 : : }
335 : :
336 : 0 : case 16: /* quad */
337 : 0 : return pass_in_gpr_lp64 (locp, size);
338 : :
339 : : default:
340 : : return -2;
341 : : }
342 : :
343 : 0 : case DW_ATE_complex_float:
344 : : /* A complex floating-point number is passed as though it were a
345 : : struct containing two floating-point reals. */
346 [ # # # # ]: 0 : switch (size)
347 : : {
348 : 0 : case 8: /* float _Complex */
349 [ # # # ]: 0 : switch (fp)
350 : : {
351 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
352 : : case EF_RISCV_FLOAT_ABI_SINGLE:
353 : 0 : return pass_in_fpr_lp64f (locp, size);
354 : 0 : case EF_RISCV_FLOAT_ABI_SOFT:
355 : : /* Double the size so the vals are two registers. */
356 : 0 : return pass_in_gpr_lp64 (locp, size * 2);
357 : : default:
358 : : return -2;
359 : : }
360 : :
361 : 0 : case 16: /* double _Complex */
362 [ # # # ]: 0 : switch (fp)
363 : : {
364 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
365 : 0 : return pass_in_fpr_lp64d (locp, size);
366 : 0 : case EF_RISCV_FLOAT_ABI_SINGLE:
367 : : case EF_RISCV_FLOAT_ABI_SOFT:
368 : 0 : return pass_in_gpr_lp64 (locp, size);
369 : : default:
370 : : return -2;
371 : : }
372 : :
373 : : case 32: /* long double _Complex */
374 : 0 : return pass_by_ref (locp);
375 : :
376 : : default:
377 : : return -2;
378 : : }
379 : : }
380 : :
381 : : return -2;
382 : : }
383 : : else
384 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
385 : : }
386 : :
387 : 0 : *locp = NULL;
388 : 0 : return 0;
389 : : }
390 : :
391 : : int
392 : 5 : riscv_return_value_location_lp64d (Dwarf_Die *functypedie,
393 : : const Dwarf_Op **locp)
394 : : {
395 : 5 : return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_DOUBLE,
396 : : functypedie, locp);
397 : : }
398 : :
399 : : int
400 : 0 : riscv_return_value_location_lp64f (Dwarf_Die *functypedie,
401 : : const Dwarf_Op **locp)
402 : : {
403 : 0 : return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_SINGLE,
404 : : functypedie, locp);
405 : : }
406 : :
407 : : int
408 : 0 : riscv_return_value_location_lp64 (Dwarf_Die *functypedie,
409 : : const Dwarf_Op **locp)
410 : : {
411 : 0 : return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_SOFT,
412 : : functypedie, locp);
413 : : }
|