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 : 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 : 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 : 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 : 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 0 : flatten_aggregate_arg (Dwarf_Die *typedie __attribute__ ((unused)),
110 : Dwarf_Die *arg0 __attribute__ ((unused)),
111 : Dwarf_Die *arg1 __attribute__ ((unused)))
112 : {
113 : /* ??? */
114 0 : return 1;
115 : }
116 :
117 : static int
118 0 : 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 0 : return -2;
125 : }
126 :
127 : int
128 0 : riscv_return_value_location_lp64d (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 : && flatten_aggregate_arg (&typedie, &arg0, &arg1))
162 : 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
174 0 : || tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
175 : {
176 0 : if (dwarf_bytesize_aux (&typedie, &size) < 0)
177 : {
178 0 : if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
179 0 : size = 8;
180 : else
181 : return -1;
182 : }
183 :
184 0 : Dwarf_Attribute attr_mem;
185 0 : if (tag == DW_TAG_base_type)
186 : {
187 0 : Dwarf_Word encoding;
188 0 : if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
189 : &attr_mem),
190 : &encoding) != 0)
191 : return -1;
192 :
193 0 : switch (encoding)
194 : {
195 0 : case DW_ATE_boolean:
196 : case DW_ATE_signed:
197 : case DW_ATE_unsigned:
198 : case DW_ATE_unsigned_char:
199 : case DW_ATE_signed_char:
200 : /* Scalars that are at most XLEN bits wide are passed in a single
201 : argument register. Scalars that are 2*XLEN bits wide are
202 : passed in a pair of argument registers. Scalars wider than
203 : 2*XLEN are passed by reference; there are none for LP64D. */
204 0 : return pass_in_gpr_lp64 (locp, size);
205 :
206 0 : case DW_ATE_float:
207 : /* A real floating-point argument is passed in a floating-point
208 : argument register if it is no more than FLEN bits wide,
209 : otherwise it is passed according to the integer calling
210 : convention. */
211 0 : switch (size)
212 : {
213 0 : case 4: /* single */
214 : case 8: /* double */
215 0 : return pass_in_fpr_lp64d (locp, size);
216 :
217 0 : case 16: /* quad */
218 0 : return pass_in_gpr_lp64 (locp, size);
219 :
220 : default:
221 : return -2;
222 : }
223 :
224 0 : case DW_ATE_complex_float:
225 : /* A complex floating-point number is passed as though it were a
226 : struct containing two floating-point reals. */
227 0 : switch (size)
228 : {
229 0 : case 8: /* float _Complex */
230 0 : return pass_in_fpr_lp64f (locp, size);
231 :
232 0 : case 16: /* double _Complex */
233 0 : return pass_in_fpr_lp64d (locp, size);
234 :
235 : case 32: /* long double _Complex */
236 0 : return pass_by_ref (locp);
237 :
238 : default:
239 : return -2;
240 : }
241 : }
242 :
243 : return -2;
244 : }
245 : else
246 0 : return pass_in_gpr_lp64 (locp, size);
247 : }
248 :
249 0 : *locp = NULL;
250 0 : return 0;
251 : }
|