xref: /aosp_15_r20/external/elfutils/backends/loongarch_retval.c (revision 7304104da70ce23c86437a01be71edd1a2d7f37e)
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