1 /* Function return value location for Linux/LoongArch ABI.
2 Copyright (C) 2013 Red Hat, Inc.
3 Copyright (C) 2023 OpenAnolis community LoongArch SIG.
4 Copyright (C) 2023 Loongson Technology Corporation Limited.
5
6 This file is part of elfutils.
7
8 This file is free software; you can redistribute it and/or modify
9 it under the terms of either
10
11 * the GNU Lesser General Public License as published by the Free
12 Software Foundation; either version 3 of the License, or (at
13 your option) any later version
14
15 or
16
17 * the GNU General Public License as published by the Free
18 Software Foundation; either version 2 of the License, or (at
19 your option) any later version
20
21 or both in parallel, as here.
22
23 elfutils is distributed in the hope that it will be useful, but
24 WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 General Public License for more details.
27
28 You should have received copies of the GNU General Public License and
29 the GNU Lesser General Public License along with this program. If
30 not, see <http://www.gnu.org/licenses/>. */
31
32 #ifdef HAVE_CONFIG_H
33 # include <config.h>
34 #endif
35
36 #include <stdio.h>
37 #include <inttypes.h>
38
39 #include <assert.h>
40 #include <dwarf.h>
41
42 #define BACKEND loongarch_
43 #include "libebl_CPU.h"
44
45 static int
dwarf_bytesize_aux(Dwarf_Die * die,Dwarf_Word * sizep)46 dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep)
47 {
48 int bits;
49 if (((bits = 8 * dwarf_bytesize (die)) < 0
50 && (bits = dwarf_bitsize (die)) < 0)
51 || bits % 8 != 0)
52 return -1;
53
54 *sizep = bits / 8;
55 return 0;
56 }
57
58 static int
pass_in_gpr(const Dwarf_Op ** locp,Dwarf_Word size)59 pass_in_gpr (const Dwarf_Op **locp, Dwarf_Word size)
60 {
61 static const Dwarf_Op loc[] =
62 {
63 { .atom = DW_OP_reg4 }, { .atom = DW_OP_piece, .number = 8 },
64 { .atom = DW_OP_reg5 }, { .atom = DW_OP_piece, .number = 8 }
65 };
66
67 *locp = loc;
68 return size <= 8 ? 1 : 4;
69 }
70
71 static int
pass_by_ref(const Dwarf_Op ** locp)72 pass_by_ref (const Dwarf_Op **locp)
73 {
74 static const Dwarf_Op loc[] = { { .atom = DW_OP_breg4 } };
75
76 *locp = loc;
77 return 1;
78 }
79
80 static int
pass_in_fpr(const Dwarf_Op ** locp,Dwarf_Word size)81 pass_in_fpr (const Dwarf_Op **locp, Dwarf_Word size)
82 {
83 static const Dwarf_Op loc[] =
84 {
85 { .atom = DW_OP_regx, .number = 32 },
86 { .atom = DW_OP_piece, .number = 8 },
87 { .atom = DW_OP_regx, .number = 33 },
88 { .atom = DW_OP_piece, .number = 8 }
89 };
90
91 *locp = loc;
92 return size <= 8 ? 1 : 4;
93 }
94
95 int
loongarch_return_value_location(Dwarf_Die * functypedie,const Dwarf_Op ** locp)96 loongarch_return_value_location(Dwarf_Die *functypedie,
97 const Dwarf_Op **locp)
98 {
99 /* Start with the function's type, and get the DW_AT_type attribute,
100 which is the type of the return value. */
101 Dwarf_Die typedie;
102 int tag = dwarf_peeled_die_type (functypedie, &typedie);
103 if (tag <= 0)
104 return tag;
105
106 Dwarf_Word size = (Dwarf_Word)-1;
107
108 /* If the argument type is a Composite Type that is larger than 16
109 bytes, then the argument is copied to memory allocated by the
110 caller and the argument is replaced by a pointer to the copy. */
111 if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type
112 || tag == DW_TAG_class_type || tag == DW_TAG_array_type)
113 {
114 if (dwarf_aggregate_size (&typedie, &size) < 0)
115 return -1;
116
117 /* Aggregates larger than 2*GRLEN bits are passed by reference. */
118 if (size > 16)
119 return pass_by_ref (locp);
120 /* Aggregates whose total size is no more than GRLEN bits are passed in
121 a register. Aggregates whose total size is no more than 2*GRLEN bits
122 are passed in a pair of registers. */
123 else
124 return pass_in_gpr (locp, size);
125 }
126
127 if (tag == DW_TAG_base_type || dwarf_is_pointer (tag))
128 {
129 if (dwarf_bytesize_aux (&typedie, &size) < 0)
130 {
131 if (dwarf_is_pointer (tag))
132 size = 8;
133 else
134 return -1;
135 }
136
137 Dwarf_Attribute attr_mem;
138 if (tag == DW_TAG_base_type)
139 {
140 Dwarf_Word encoding;
141 if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
142 &attr_mem),
143 &encoding) != 0)
144 return -1;
145
146 switch (encoding)
147 {
148 case DW_ATE_boolean:
149 case DW_ATE_signed:
150 case DW_ATE_unsigned:
151 case DW_ATE_unsigned_char:
152 case DW_ATE_signed_char:
153 /* Scalars that are at most GRLEN bits wide are passed in a single
154 argument register. Scalars that are 2*GRLEN bits wide are
155 passed in a pair of argument registers. Scalars wider than
156 2*GRLEN are passed by reference. */
157 return pass_in_gpr (locp, size);
158
159 case DW_ATE_float:
160 /* A real floating-point argument is passed in a floating-point
161 argument register if it is no more than FLEN bits wide,
162 otherwise it is passed according to the integer calling
163 convention. */
164 switch (size)
165 {
166 case 4: /* single */
167 case 8: /* double */
168 return pass_in_fpr (locp, size);
169
170 case 16: /* quad */
171 return pass_in_gpr (locp, size);
172
173 default:
174 return -2;
175 }
176
177 case DW_ATE_complex_float:
178 /* A complex floating-point number is passed as though it were a
179 struct containing two floating-point reals. */
180 switch (size)
181 {
182 case 8: /* float _Complex */
183 case 16: /* double _Complex */
184 return pass_in_fpr (locp, size);
185
186 case 32: /* long double _Complex */
187 return pass_by_ref (locp);
188
189 default:
190 return -2;
191 }
192 }
193
194 return -2;
195 }
196 else
197 return pass_in_gpr (locp, size);
198 }
199
200 *locp = NULL;
201 return 0;
202 }
203