/src/elfutils/backends/riscv_retval.c
Line | Count | Source (jump to first uncovered line) |
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 | | dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep) |
46 | 0 | { |
47 | 0 | int bits; |
48 | 0 | if (((bits = 8 * dwarf_bytesize (die)) < 0 |
49 | 0 | && (bits = dwarf_bitsize (die)) < 0) |
50 | 0 | || bits % 8 != 0) |
51 | 0 | return -1; |
52 | | |
53 | 0 | *sizep = bits / 8; |
54 | 0 | return 0; |
55 | 0 | } |
56 | | |
57 | | static int |
58 | | pass_in_gpr_lp64 (const Dwarf_Op **locp, Dwarf_Word size) |
59 | 0 | { |
60 | 0 | static const Dwarf_Op loc[] = |
61 | 0 | { |
62 | 0 | { .atom = DW_OP_reg10 }, { .atom = DW_OP_piece, .number = 8 }, |
63 | 0 | { .atom = DW_OP_reg11 }, { .atom = DW_OP_piece, .number = 8 } |
64 | 0 | }; |
65 | |
|
66 | 0 | *locp = loc; |
67 | 0 | return size <= 8 ? 1 : 4; |
68 | 0 | } |
69 | | |
70 | | static int |
71 | | pass_by_ref (const Dwarf_Op **locp) |
72 | 0 | { |
73 | 0 | static const Dwarf_Op loc[] = { { .atom = DW_OP_breg10 } }; |
74 | |
|
75 | 0 | *locp = loc; |
76 | 0 | return 1; |
77 | 0 | } |
78 | | |
79 | | static int |
80 | | pass_in_fpr_lp64f (const Dwarf_Op **locp, Dwarf_Word size) |
81 | 0 | { |
82 | 0 | static const Dwarf_Op loc[] = |
83 | 0 | { |
84 | 0 | { .atom = DW_OP_regx, .number = 42 }, |
85 | 0 | { .atom = DW_OP_piece, .number = 4 }, |
86 | 0 | { .atom = DW_OP_regx, .number = 43 }, |
87 | 0 | { .atom = DW_OP_piece, .number = 4 } |
88 | 0 | }; |
89 | |
|
90 | 0 | *locp = loc; |
91 | 0 | return size <= 4 ? 1 : 4; |
92 | 0 | } |
93 | | |
94 | | static int |
95 | | pass_in_fpr_lp64d (const Dwarf_Op **locp, Dwarf_Word size) |
96 | 0 | { |
97 | 0 | static const Dwarf_Op loc[] = |
98 | 0 | { |
99 | 0 | { .atom = DW_OP_regx, .number = 42 }, |
100 | 0 | { .atom = DW_OP_piece, .number = 8 }, |
101 | 0 | { .atom = DW_OP_regx, .number = 43 }, |
102 | 0 | { .atom = DW_OP_piece, .number = 8 } |
103 | 0 | }; |
104 | |
|
105 | 0 | *locp = loc; |
106 | 0 | return size <= 8 ? 1 : 4; |
107 | 0 | } |
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 | | flatten_aggregate_arg (Dwarf_Die *typedie, |
114 | | Dwarf_Word size, |
115 | | Dwarf_Die *arg0, |
116 | | Dwarf_Die *arg1) |
117 | 0 | { |
118 | 0 | int tag0, tag1; |
119 | 0 | Dwarf_Die member; |
120 | 0 | Dwarf_Word encoding0, encoding1; |
121 | 0 | Dwarf_Attribute attr; |
122 | 0 | Dwarf_Word size0, size1; |
123 | |
|
124 | 0 | if (size < 8 || size > 16) |
125 | 0 | return 0; |
126 | | |
127 | 0 | if (dwarf_child (typedie, arg0) != 0) |
128 | 0 | return 0; |
129 | | |
130 | 0 | tag0 = dwarf_tag (arg0); |
131 | 0 | while (tag0 != -1 && tag0 != DW_TAG_member) |
132 | 0 | { |
133 | 0 | if (dwarf_siblingof (arg0, arg0) != 0) |
134 | 0 | return 0; |
135 | 0 | tag0 = dwarf_tag (arg0); |
136 | 0 | } |
137 | | |
138 | 0 | if (tag0 != DW_TAG_member) |
139 | 0 | return 0; |
140 | | |
141 | | /* Remember where we are. */ |
142 | 0 | member = *arg0; |
143 | |
|
144 | 0 | tag0 = dwarf_peeled_die_type (arg0, arg0); |
145 | 0 | if (tag0 != DW_TAG_base_type) |
146 | 0 | return 0; |
147 | | |
148 | 0 | if (dwarf_attr_integrate (arg0, DW_AT_encoding, &attr) == NULL |
149 | 0 | || dwarf_formudata (&attr, &encoding0) != 0) |
150 | 0 | return 0; |
151 | | |
152 | 0 | if (dwarf_bytesize_aux (arg0, &size0) != 0) |
153 | 0 | return 0; |
154 | | |
155 | 0 | if (size == size0) |
156 | 0 | return 1; /* This one member is the whole size. */ |
157 | | |
158 | 0 | if (size != 2 * size0) |
159 | 0 | return 0; /* We only handle two of the same. */ |
160 | | |
161 | | /* Look for another member with the same encoding. */ |
162 | 0 | if (dwarf_siblingof (&member, arg1) != 0) |
163 | 0 | return 0; |
164 | | |
165 | 0 | tag1 = dwarf_tag (arg1); |
166 | 0 | while (tag1 != -1 && tag1 != DW_TAG_member) |
167 | 0 | { |
168 | 0 | if (dwarf_siblingof (arg1, arg1) != 0) |
169 | 0 | return 0; |
170 | 0 | tag1 = dwarf_tag (arg1); |
171 | 0 | } |
172 | | |
173 | 0 | if (tag1 != DW_TAG_member) |
174 | 0 | return 0; |
175 | | |
176 | 0 | tag1 = dwarf_peeled_die_type (arg1, arg1); |
177 | 0 | if (tag1 != DW_TAG_base_type) |
178 | 0 | return 0; /* We can only handle two equal base types for now. */ |
179 | | |
180 | 0 | if (dwarf_attr_integrate (arg1, DW_AT_encoding, &attr) == NULL |
181 | 0 | || dwarf_formudata (&attr, &encoding1) != 0 |
182 | 0 | || encoding0 != encoding1) |
183 | 0 | return 0; /* We can only handle two of the same for now. */ |
184 | | |
185 | 0 | if (dwarf_bytesize_aux (arg1, &size1) != 0) |
186 | 0 | return 0; |
187 | | |
188 | 0 | if (size0 != size1) |
189 | 0 | return 0; /* We can only handle two of the same for now. */ |
190 | | |
191 | 0 | return 1; |
192 | 0 | } |
193 | | |
194 | | /* arg0 and arg1 should be the peeled die types found by |
195 | | flatten_aggregate_arg. */ |
196 | | static int |
197 | | pass_by_flattened_arg (const Dwarf_Op **locp, |
198 | | Dwarf_Word size, |
199 | | Dwarf_Die *arg0, |
200 | | Dwarf_Die *arg1 __attribute__((unused))) |
201 | 0 | { |
202 | | /* For now we just assume arg0 and arg1 are the same type and |
203 | | encoding. */ |
204 | 0 | Dwarf_Word encoding; |
205 | 0 | Dwarf_Attribute attr; |
206 | |
|
207 | 0 | if (dwarf_attr_integrate (arg0, DW_AT_encoding, &attr) == NULL |
208 | 0 | || dwarf_formudata (&attr, &encoding) != 0) |
209 | 0 | return -1; |
210 | | |
211 | 0 | switch (encoding) |
212 | 0 | { |
213 | 0 | case DW_ATE_boolean: |
214 | 0 | case DW_ATE_signed: |
215 | 0 | case DW_ATE_unsigned: |
216 | 0 | case DW_ATE_unsigned_char: |
217 | 0 | case DW_ATE_signed_char: |
218 | 0 | return pass_in_gpr_lp64 (locp, size); |
219 | | |
220 | 0 | case DW_ATE_float: |
221 | 0 | return pass_in_fpr_lp64d (locp, size); |
222 | | |
223 | 0 | default: |
224 | 0 | return -1; |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | int |
229 | | riscv_return_value_location_lp64ifd (int fp, Dwarf_Die *functypedie, |
230 | | const Dwarf_Op **locp) |
231 | 0 | { |
232 | | /* Start with the function's type, and get the DW_AT_type attribute, |
233 | | which is the type of the return value. */ |
234 | 0 | Dwarf_Die typedie; |
235 | 0 | int tag = dwarf_peeled_die_type (functypedie, &typedie); |
236 | 0 | if (tag <= 0) |
237 | 0 | return tag; |
238 | | |
239 | 0 | 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 | 0 | if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type |
245 | 0 | || tag == DW_TAG_class_type || tag == DW_TAG_array_type) |
246 | 0 | { |
247 | 0 | Dwarf_Die arg0, arg1; |
248 | |
|
249 | 0 | if (dwarf_aggregate_size (&typedie, &size) < 0) |
250 | 0 | 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 | 0 | if (tag == DW_TAG_structure_type |
262 | 0 | && flatten_aggregate_arg (&typedie, size, &arg0, &arg1)) |
263 | 0 | 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 | 0 | else |
271 | 0 | return pass_in_gpr_lp64 (locp, size); |
272 | 0 | } |
273 | | |
274 | 0 | if (tag == DW_TAG_base_type || dwarf_is_pointer (tag)) |
275 | 0 | { |
276 | 0 | if (dwarf_bytesize_aux (&typedie, &size) < 0) |
277 | 0 | { |
278 | 0 | if (dwarf_is_pointer (tag)) |
279 | 0 | size = 8; |
280 | 0 | else |
281 | 0 | return -1; |
282 | 0 | } |
283 | | |
284 | 0 | Dwarf_Attribute attr_mem; |
285 | 0 | if (tag == DW_TAG_base_type) |
286 | 0 | { |
287 | 0 | Dwarf_Word encoding; |
288 | 0 | if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding, |
289 | 0 | &attr_mem), |
290 | 0 | &encoding) != 0) |
291 | 0 | return -1; |
292 | | |
293 | 0 | switch (encoding) |
294 | 0 | { |
295 | 0 | case DW_ATE_boolean: |
296 | 0 | case DW_ATE_signed: |
297 | 0 | case DW_ATE_unsigned: |
298 | 0 | case DW_ATE_unsigned_char: |
299 | 0 | 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 | 0 | 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 | 0 | { |
313 | 0 | case 4: /* single */ |
314 | 0 | switch (fp) |
315 | 0 | { |
316 | 0 | case EF_RISCV_FLOAT_ABI_DOUBLE: |
317 | 0 | 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 | 0 | default: |
322 | 0 | return -2; |
323 | 0 | } |
324 | 0 | case 8: /* double */ |
325 | 0 | switch (fp) |
326 | 0 | { |
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 | 0 | case EF_RISCV_FLOAT_ABI_SOFT: |
331 | 0 | return pass_in_gpr_lp64 (locp, size); |
332 | 0 | default: |
333 | 0 | return -2; |
334 | 0 | } |
335 | | |
336 | 0 | case 16: /* quad */ |
337 | 0 | return pass_in_gpr_lp64 (locp, size); |
338 | | |
339 | 0 | default: |
340 | 0 | return -2; |
341 | 0 | } |
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 | 0 | { |
348 | 0 | case 8: /* float _Complex */ |
349 | 0 | switch (fp) |
350 | 0 | { |
351 | 0 | case EF_RISCV_FLOAT_ABI_DOUBLE: |
352 | 0 | 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 | 0 | default: |
358 | 0 | return -2; |
359 | 0 | } |
360 | | |
361 | 0 | case 16: /* double _Complex */ |
362 | 0 | switch (fp) |
363 | 0 | { |
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 | 0 | case EF_RISCV_FLOAT_ABI_SOFT: |
368 | 0 | return pass_in_gpr_lp64 (locp, size); |
369 | 0 | default: |
370 | 0 | return -2; |
371 | 0 | } |
372 | | |
373 | 0 | case 32: /* long double _Complex */ |
374 | 0 | return pass_by_ref (locp); |
375 | | |
376 | 0 | default: |
377 | 0 | return -2; |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | 0 | return -2; |
382 | 0 | } |
383 | 0 | else |
384 | 0 | return pass_in_gpr_lp64 (locp, size); |
385 | 0 | } |
386 | | |
387 | 0 | *locp = NULL; |
388 | 0 | return 0; |
389 | 0 | } |
390 | | |
391 | | int |
392 | | riscv_return_value_location_lp64d (Dwarf_Die *functypedie, |
393 | | const Dwarf_Op **locp) |
394 | 0 | { |
395 | 0 | return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_DOUBLE, |
396 | 0 | functypedie, locp); |
397 | 0 | } |
398 | | |
399 | | int |
400 | | riscv_return_value_location_lp64f (Dwarf_Die *functypedie, |
401 | | const Dwarf_Op **locp) |
402 | 0 | { |
403 | 0 | return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_SINGLE, |
404 | 0 | functypedie, locp); |
405 | 0 | } |
406 | | |
407 | | int |
408 | | riscv_return_value_location_lp64 (Dwarf_Die *functypedie, |
409 | | const Dwarf_Op **locp) |
410 | 0 | { |
411 | 0 | return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_SOFT, |
412 | 0 | functypedie, locp); |
413 | 0 | } |