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 : : This file is part of elfutils.
5 : :
6 : : This file is free software; you can redistribute it and/or modify
7 : : it under the terms of either
8 : :
9 : : * the GNU Lesser General Public License as published by the Free
10 : : Software Foundation; either version 3 of the License, or (at
11 : : your option) any later version
12 : :
13 : : or
14 : :
15 : : * the GNU General Public License as published by the Free
16 : : Software Foundation; either version 2 of the License, or (at
17 : : your option) any later version
18 : :
19 : : or both in parallel, as here.
20 : :
21 : : elfutils is distributed in the hope that it will be useful, but
22 : : WITHOUT ANY WARRANTY; without even the implied warranty of
23 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 : : General Public License for more details.
25 : :
26 : : You should have received copies of the GNU General Public License and
27 : : the GNU Lesser General Public License along with this program. If
28 : : not, see <http://www.gnu.org/licenses/>. */
29 : :
30 : : #ifdef HAVE_CONFIG_H
31 : : # include <config.h>
32 : : #endif
33 : :
34 : : #include <stdio.h>
35 : : #include <inttypes.h>
36 : :
37 : : #include <assert.h>
38 : : #include <dwarf.h>
39 : :
40 : : #define BACKEND riscv_
41 : : #include "libebl_CPU.h"
42 : :
43 : : static int
44 : 0 : dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep)
45 : : {
46 : 0 : int bits;
47 [ # # ]: 0 : if (((bits = 8 * dwarf_bytesize (die)) < 0
48 [ # # ]: 0 : && (bits = dwarf_bitsize (die)) < 0)
49 [ # # ]: 0 : || bits % 8 != 0)
50 : 0 : return -1;
51 : :
52 : 0 : *sizep = bits / 8;
53 : 0 : return 0;
54 : : }
55 : :
56 : : static int
57 : 0 : pass_in_gpr_lp64 (const Dwarf_Op **locp, Dwarf_Word size)
58 : : {
59 : 0 : static const Dwarf_Op loc[] =
60 : : {
61 : : { .atom = DW_OP_reg10 }, { .atom = DW_OP_piece, .number = 8 },
62 : : { .atom = DW_OP_reg11 }, { .atom = DW_OP_piece, .number = 8 }
63 : : };
64 : :
65 : 0 : *locp = loc;
66 : 0 : return size <= 8 ? 1 : 4;
67 : : }
68 : :
69 : : static int
70 : 0 : pass_by_ref (const Dwarf_Op **locp)
71 : : {
72 : 0 : static const Dwarf_Op loc[] = { { .atom = DW_OP_breg10 } };
73 : :
74 : 0 : *locp = loc;
75 : 0 : return 1;
76 : : }
77 : :
78 : : static int
79 : 0 : pass_in_fpr_lp64f (const Dwarf_Op **locp, Dwarf_Word size)
80 : : {
81 : 0 : static const Dwarf_Op loc[] =
82 : : {
83 : : { .atom = DW_OP_regx, .number = 42 },
84 : : { .atom = DW_OP_piece, .number = 4 },
85 : : { .atom = DW_OP_regx, .number = 43 },
86 : : { .atom = DW_OP_piece, .number = 4 }
87 : : };
88 : :
89 : 0 : *locp = loc;
90 : 0 : return size <= 4 ? 1 : 4;
91 : : }
92 : :
93 : : static int
94 : 0 : pass_in_fpr_lp64d (const Dwarf_Op **locp, Dwarf_Word size)
95 : : {
96 : 0 : static const Dwarf_Op loc[] =
97 : : {
98 : : { .atom = DW_OP_regx, .number = 42 },
99 : : { .atom = DW_OP_piece, .number = 8 },
100 : : { .atom = DW_OP_regx, .number = 43 },
101 : : { .atom = DW_OP_piece, .number = 8 }
102 : : };
103 : :
104 : 0 : *locp = loc;
105 : 0 : return size <= 8 ? 1 : 4;
106 : : }
107 : :
108 : : static int
109 : : flatten_aggregate_arg (Dwarf_Die *typedie __attribute__ ((unused)),
110 : : Dwarf_Die *arg0 __attribute__ ((unused)),
111 : : Dwarf_Die *arg1 __attribute__ ((unused)))
112 : : {
113 : : /* ??? */
114 : : return 1;
115 : : }
116 : :
117 : : static int
118 : : pass_by_flattened_arg (const Dwarf_Op **locp __attribute__ ((unused)),
119 : : Dwarf_Word size __attribute__ ((unused)),
120 : : Dwarf_Die *arg0 __attribute__ ((unused)),
121 : : Dwarf_Die *arg1 __attribute__ ((unused)))
122 : : {
123 : : /* ??? */
124 : : return -2;
125 : : }
126 : :
127 : : int
128 : 0 : riscv_return_value_location_lp64ifd (int fp, Dwarf_Die *functypedie,
129 : : const Dwarf_Op **locp)
130 : : {
131 : : /* Start with the function's type, and get the DW_AT_type attribute,
132 : : which is the type of the return value. */
133 : 0 : Dwarf_Die typedie;
134 : 0 : int tag = dwarf_peeled_die_type (functypedie, &typedie);
135 [ # # ]: 0 : if (tag <= 0)
136 : : return tag;
137 : :
138 : 0 : Dwarf_Word size = (Dwarf_Word)-1;
139 : :
140 : : /* If the argument type is a Composite Type that is larger than 16
141 : : bytes, then the argument is copied to memory allocated by the
142 : : caller and the argument is replaced by a pointer to the copy. */
143 [ # # ]: 0 : if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type
144 [ # # ]: 0 : || tag == DW_TAG_class_type || tag == DW_TAG_array_type)
145 : : {
146 : 0 : Dwarf_Die arg0, arg1;
147 : :
148 [ # # ]: 0 : if (dwarf_aggregate_size (&typedie, &size) < 0)
149 : : return -1;
150 : : /* A struct containing just one floating-point real is passed as though
151 : : it were a standalone floating-point real. A struct containing two
152 : : floating-point reals is passed in two floating-point registers, if
153 : : neither is more than FLEN bits wide. A struct containing just one
154 : : complex floating-point number is passed as though it were a struct
155 : : containing two floating-point reals. A struct containing one
156 : : floating-point real and one integer (or bitfield), in either order,
157 : : is passed in a floating-point register and an integer register,
158 : : provided the floating-point real is no more than FLEN bits wide and
159 : : the integer is no more than XLEN bits wide. */
160 [ # # ]: 0 : if (tag == DW_TAG_structure_type
161 : 0 : && flatten_aggregate_arg (&typedie, &arg0, &arg1))
162 : 0 : return pass_by_flattened_arg (locp, size, &arg0, &arg1);
163 : : /* Aggregates larger than 2*XLEN bits are passed by reference. */
164 [ # # ]: 0 : else if (size > 16)
165 : 0 : return pass_by_ref (locp);
166 : : /* Aggregates whose total size is no more than XLEN bits are passed in
167 : : a register. Aggregates whose total size is no more than 2*XLEN bits
168 : : are passed in a pair of registers. */
169 : : else
170 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
171 : : }
172 : :
173 [ # # # # ]: 0 : if (tag == DW_TAG_base_type || dwarf_is_pointer (tag))
174 : : {
175 [ # # ]: 0 : if (dwarf_bytesize_aux (&typedie, &size) < 0)
176 : : {
177 [ # # ]: 0 : if (dwarf_is_pointer (tag))
178 : 0 : size = 8;
179 : : else
180 : : return -1;
181 : : }
182 : :
183 : 0 : Dwarf_Attribute attr_mem;
184 [ # # ]: 0 : if (tag == DW_TAG_base_type)
185 : : {
186 : 0 : Dwarf_Word encoding;
187 [ # # ]: 0 : if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
188 : : &attr_mem),
189 : : &encoding) != 0)
190 : : return -1;
191 : :
192 [ # # # # ]: 0 : switch (encoding)
193 : : {
194 : 0 : case DW_ATE_boolean:
195 : : case DW_ATE_signed:
196 : : case DW_ATE_unsigned:
197 : : case DW_ATE_unsigned_char:
198 : : case DW_ATE_signed_char:
199 : : /* Scalars that are at most XLEN bits wide are passed in a single
200 : : argument register. Scalars that are 2*XLEN bits wide are
201 : : passed in a pair of argument registers. Scalars wider than
202 : : 2*XLEN are passed by reference; there are none for LP64D. */
203 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
204 : :
205 : 0 : case DW_ATE_float:
206 : : /* A real floating-point argument is passed in a floating-point
207 : : argument register if it is no more than FLEN bits wide,
208 : : otherwise it is passed according to the integer calling
209 : : convention. */
210 [ # # # # ]: 0 : switch (size)
211 : : {
212 : 0 : case 4: /* single */
213 [ # # # ]: 0 : switch (fp)
214 : : {
215 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
216 : : case EF_RISCV_FLOAT_ABI_SINGLE:
217 [ # # ]: 0 : return pass_in_fpr_lp64d (locp, size);
218 : 0 : case EF_RISCV_FLOAT_ABI_SOFT:
219 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
220 : : default:
221 : : return -2;
222 : : }
223 : 0 : case 8: /* double */
224 [ # # # ]: 0 : switch (fp)
225 : : {
226 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
227 [ # # ]: 0 : return pass_in_fpr_lp64d (locp, size);
228 : 0 : case EF_RISCV_FLOAT_ABI_SINGLE:
229 : : case EF_RISCV_FLOAT_ABI_SOFT:
230 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
231 : : default:
232 : : return -2;
233 : : }
234 : :
235 : 0 : case 16: /* quad */
236 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
237 : :
238 : : default:
239 : : return -2;
240 : : }
241 : :
242 : 0 : case DW_ATE_complex_float:
243 : : /* A complex floating-point number is passed as though it were a
244 : : struct containing two floating-point reals. */
245 [ # # # # ]: 0 : switch (size)
246 : : {
247 : 0 : case 8: /* float _Complex */
248 [ # # # ]: 0 : switch (fp)
249 : : {
250 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
251 : : case EF_RISCV_FLOAT_ABI_SINGLE:
252 [ # # ]: 0 : return pass_in_fpr_lp64f (locp, size);
253 : 0 : case EF_RISCV_FLOAT_ABI_SOFT:
254 : : /* Double the size so the vals are two registers. */
255 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size * 2);
256 : : default:
257 : : return -2;
258 : : }
259 : :
260 : 0 : case 16: /* double _Complex */
261 [ # # # ]: 0 : switch (fp)
262 : : {
263 : 0 : case EF_RISCV_FLOAT_ABI_DOUBLE:
264 [ # # ]: 0 : return pass_in_fpr_lp64d (locp, size);
265 : 0 : case EF_RISCV_FLOAT_ABI_SINGLE:
266 : : case EF_RISCV_FLOAT_ABI_SOFT:
267 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
268 : : default:
269 : : return -2;
270 : : }
271 : :
272 : : case 32: /* long double _Complex */
273 : 0 : return pass_by_ref (locp);
274 : :
275 : : default:
276 : : return -2;
277 : : }
278 : : }
279 : :
280 : : return -2;
281 : : }
282 : : else
283 [ # # ]: 0 : return pass_in_gpr_lp64 (locp, size);
284 : : }
285 : :
286 : 0 : *locp = NULL;
287 : 0 : return 0;
288 : : }
289 : :
290 : : int
291 : 0 : riscv_return_value_location_lp64d (Dwarf_Die *functypedie,
292 : : const Dwarf_Op **locp)
293 : : {
294 : 0 : return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_DOUBLE,
295 : : functypedie, locp);
296 : : }
297 : :
298 : : int
299 : 0 : riscv_return_value_location_lp64f (Dwarf_Die *functypedie,
300 : : const Dwarf_Op **locp)
301 : : {
302 : 0 : return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_SINGLE,
303 : : functypedie, locp);
304 : : }
305 : :
306 : : int
307 : 0 : riscv_return_value_location_lp64 (Dwarf_Die *functypedie,
308 : : const Dwarf_Op **locp)
309 : : {
310 : 0 : return riscv_return_value_location_lp64ifd (EF_RISCV_FLOAT_ABI_SOFT,
311 : : functypedie, locp);
312 : : }
|