1 /* Function return value location for IA64 ABI.
2    Copyright (C) 2006-2010, 2014 Red Hat, Inc.
3    This file is part of elfutils.
4 
5    This file is free software; you can redistribute it and/or modify
6    it under the terms of either
7 
8      * the GNU Lesser General Public License as published by the Free
9        Software Foundation; either version 3 of the License, or (at
10        your option) any later version
11 
12    or
13 
14      * the GNU General Public License as published by the Free
15        Software Foundation; either version 2 of the License, or (at
16        your option) any later version
17 
18    or both in parallel, as here.
19 
20    elfutils is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    General Public License for more details.
24 
25    You should have received copies of the GNU General Public License and
26    the GNU Lesser General Public License along with this program.  If
27    not, see <http://www.gnu.org/licenses/>.  */
28 
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32 
33 #include <assert.h>
34 #include <dwarf.h>
35 
36 #define BACKEND ia64_
37 #include "libebl_CPU.h"
38 
39 
40 /* r8, or pair r8, r9, or aggregate up to r8-r11.  */
41 static const Dwarf_Op loc_intreg[] =
42   {
43     { .atom = DW_OP_reg8 }, { .atom = DW_OP_piece, .number = 8 },
44     { .atom = DW_OP_reg9 }, { .atom = DW_OP_piece, .number = 8 },
45     { .atom = DW_OP_reg10 }, { .atom = DW_OP_piece, .number = 8 },
46     { .atom = DW_OP_reg11 }, { .atom = DW_OP_piece, .number = 8 },
47   };
48 #define nloc_intreg	1
49 #define nloc_intregs(n)	(2 * (n))
50 
51 /* f8, or aggregate up to f8-f15.  */
52 #define DEFINE_FPREG(size) 						      \
53   static const Dwarf_Op loc_fpreg_##size[] =				      \
54     {									      \
55       { .atom = DW_OP_regx, .number = 128 + 8 },			      \
56       { .atom = DW_OP_piece, .number = size },				      \
57       { .atom = DW_OP_regx, .number = 128 + 9 },			      \
58       { .atom = DW_OP_piece, .number = size },				      \
59       { .atom = DW_OP_regx, .number = 128 + 10 },			      \
60       { .atom = DW_OP_piece, .number = size },				      \
61       { .atom = DW_OP_regx, .number = 128 + 11 },			      \
62       { .atom = DW_OP_piece, .number = size },				      \
63       { .atom = DW_OP_regx, .number = 128 + 12 },			      \
64       { .atom = DW_OP_piece, .number = size },				      \
65       { .atom = DW_OP_regx, .number = 128 + 13 },			      \
66       { .atom = DW_OP_piece, .number = size },				      \
67       { .atom = DW_OP_regx, .number = 128 + 14 },			      \
68       { .atom = DW_OP_piece, .number = size },				      \
69       { .atom = DW_OP_regx, .number = 128 + 15 },			      \
70       { .atom = DW_OP_piece, .number = size },				      \
71     }
72 #define nloc_fpreg	1
73 #define nloc_fpregs(n)	(2 * (n))
74 
75 DEFINE_FPREG (4);
76 DEFINE_FPREG (8);
77 DEFINE_FPREG (10);
78 
79 #undef DEFINE_FPREG
80 
81 
82 /* The return value is a structure and is actually stored in stack space
83    passed in a hidden argument by the caller.  But, the compiler
84    helpfully returns the address of that space in r8.  */
85 static const Dwarf_Op loc_aggregate[] =
86   {
87     { .atom = DW_OP_breg8, .number = 0 }
88   };
89 #define nloc_aggregate 1
90 
91 
92 /* If this type is an HFA small enough to be returned in FP registers,
93    return the number of registers to use.  Otherwise 9, or -1 for errors.  */
94 static int
hfa_type(Dwarf_Die * typedie,Dwarf_Word size,const Dwarf_Op ** locp,int fpregs_used)95 hfa_type (Dwarf_Die *typedie, Dwarf_Word size,
96 	  const Dwarf_Op **locp, int fpregs_used)
97 {
98   /* Descend the type structure, counting elements and finding their types.
99      If we find a datum that's not an FP type (and not quad FP), punt.
100      If we find a datum that's not the same FP type as the first datum, punt.
101      If we count more than eight total homogeneous FP data, punt.  */
102 
103   inline int hfa (const Dwarf_Op *loc, int nregs)
104     {
105       if (fpregs_used == 0)
106 	*locp = loc;
107       else if (*locp != loc)
108 	return 9;
109       return fpregs_used + nregs;
110     }
111 
112   int tag = DWARF_TAG_OR_RETURN (typedie);
113   switch (tag)
114     {
115       Dwarf_Attribute attr_mem;
116 
117     case -1:
118       return -1;
119 
120     case DW_TAG_base_type:;
121       Dwarf_Word encoding;
122       if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
123 						 &attr_mem), &encoding) != 0)
124 	return -1;
125 
126       switch (encoding)
127 	{
128 	case DW_ATE_float:
129 	  switch (size)
130 	    {
131 	    case 4:		/* float */
132 	      return hfa (loc_fpreg_4, 1);
133 	    case 8:		/* double */
134 	      return hfa (loc_fpreg_8, 1);
135 	    case 10:       /* x86-style long double, not really used */
136 	      return hfa (loc_fpreg_10, 1);
137 	    }
138 	  break;
139 
140 	case DW_ATE_complex_float:
141 	  switch (size)
142 	    {
143 	    case 4 * 2:	/* complex float */
144 	      return hfa (loc_fpreg_4, 2);
145 	    case 8 * 2:	/* complex double */
146 	      return hfa (loc_fpreg_8, 2);
147 	    case 10 * 2:	/* complex long double (x86-style) */
148 	      return hfa (loc_fpreg_10, 2);
149 	    }
150 	  break;
151 	}
152       break;
153 
154     case DW_TAG_structure_type:
155     case DW_TAG_class_type:
156     case DW_TAG_union_type:;
157       Dwarf_Die child_mem;
158       switch (dwarf_child (typedie, &child_mem))
159 	{
160 	default:
161 	  return -1;
162 
163 	case 1:			/* No children: empty struct.  */
164 	  break;
165 
166 	case 0:;		/* Look at each element.  */
167 	  int max_used = fpregs_used;
168 	  do
169 	    switch (dwarf_tag (&child_mem))
170 	      {
171 	      case -1:
172 		return -1;
173 
174 	      case DW_TAG_member:;
175 		Dwarf_Die child_type_mem;
176 		Dwarf_Die *child_typedie
177 		  = dwarf_formref_die (dwarf_attr_integrate (&child_mem,
178 							     DW_AT_type,
179 							     &attr_mem),
180 				       &child_type_mem);
181 		Dwarf_Word child_size;
182 		if (dwarf_aggregate_size (child_typedie, &child_size) != 0)
183 		  return -1;
184 		if (tag == DW_TAG_union_type)
185 		  {
186 		    int used = hfa_type (child_typedie, child_size,
187 					 locp, fpregs_used);
188 		    if (used < 0 || used > 8)
189 		      return used;
190 		    if (used > max_used)
191 		      max_used = used;
192 		  }
193 		else
194 		  {
195 		    fpregs_used = hfa_type (child_typedie, child_size,
196 					    locp, fpregs_used);
197 		    if (fpregs_used < 0 || fpregs_used > 8)
198 		      return fpregs_used;
199 		  }
200 	      }
201 	  while (dwarf_siblingof (&child_mem, &child_mem) == 0);
202 	  if (tag == DW_TAG_union_type)
203 	    fpregs_used = max_used;
204 	  break;
205 	}
206       break;
207 
208     case DW_TAG_array_type:
209       if (size == 0)
210 	break;
211 
212       Dwarf_Die base_type_mem;
213       Dwarf_Die *base_typedie
214 	= dwarf_formref_die (dwarf_attr_integrate (typedie, DW_AT_type,
215 						   &attr_mem),
216 			     &base_type_mem);
217       Dwarf_Word base_size;
218       if (dwarf_aggregate_size (base_typedie, &base_size) != 0)
219 	return -1;
220 
221       int used = hfa_type (base_typedie, base_size, locp, 0);
222       if (used < 0 || used > 8)
223 	return used;
224       if (size % (*locp)[1].number != 0)
225 	return 0;
226       fpregs_used += used * (size / (*locp)[1].number);
227       break;
228 
229     default:
230       return 9;
231     }
232 
233   return fpregs_used;
234 }
235 
236 int
ia64_return_value_location(Dwarf_Die * functypedie,const Dwarf_Op ** locp)237 ia64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
238 {
239   /* Start with the function's type, and get the DW_AT_type attribute,
240      which is the type of the return value.  */
241   Dwarf_Die die_mem, *typedie = &die_mem;
242   int tag = dwarf_peeled_die_type (functypedie, typedie);
243   if (tag <= 0)
244     return tag;
245 
246   Dwarf_Word size;
247   switch (tag)
248     {
249     case -1:
250       return -1;
251 
252     case DW_TAG_subrange_type:
253       if (! dwarf_hasattr_integrate (typedie, DW_AT_byte_size))
254 	{
255 	  Dwarf_Attribute attr_mem, *attr;
256 	  attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
257 	  typedie = dwarf_formref_die (attr, &die_mem);
258 	  tag = DWARF_TAG_OR_RETURN (typedie);
259 	}
260       /* Fall through.  */
261 
262     case DW_TAG_base_type:
263     case DW_TAG_enumeration_type:
264     case DW_TAG_pointer_type:
265     case DW_TAG_ptr_to_member_type:
266       {
267 	Dwarf_Attribute attr_mem;
268 	if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_byte_size,
269 						   &attr_mem), &size) != 0)
270 	  {
271 	    if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
272 	      size = 8;
273 	    else
274 	      return -1;
275 	  }
276       }
277 
278       if (tag == DW_TAG_base_type)
279 	{
280 	  Dwarf_Attribute attr_mem;
281 	  Dwarf_Word encoding;
282 	  if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
283 						     &attr_mem),
284 			       &encoding) != 0)
285 	    return -1;
286 
287 	  switch (encoding)
288 	    {
289 	    case DW_ATE_float:
290 	      switch (size)
291 		{
292 		case 4:		/* float */
293 		  *locp = loc_fpreg_4;
294 		  return nloc_fpreg;
295 		case 8:		/* double */
296 		  *locp = loc_fpreg_8;
297 		  return nloc_fpreg;
298 		case 10:       /* x86-style long double, not really used */
299 		  *locp = loc_fpreg_10;
300 		  return nloc_fpreg;
301 		case 16:	/* long double, IEEE quad format */
302 		  *locp = loc_intreg;
303 		  return nloc_intregs (2);
304 		}
305 	      return -2;
306 
307 	    case DW_ATE_complex_float:
308 	      switch (size)
309 		{
310 		case 4 * 2:	/* complex float */
311 		  *locp = loc_fpreg_4;
312 		  return nloc_fpregs (2);
313 		case 8 * 2:	/* complex double */
314 		  *locp = loc_fpreg_8;
315 		  return nloc_fpregs (2);
316 		case 10 * 2:	/* complex long double (x86-style) */
317 		  *locp = loc_fpreg_10;
318 		  return nloc_fpregs (2);
319 		case 16 * 2:	/* complex long double (IEEE quad) */
320 		  *locp = loc_intreg;
321 		  return nloc_intregs (4);
322 		}
323 	      return -2;
324 	    }
325 	}
326 
327     intreg:
328       *locp = loc_intreg;
329       if (size <= 8)
330 	return nloc_intreg;
331       if (size <= 32)
332 	return nloc_intregs ((size + 7) / 8);
333 
334     large:
335       *locp = loc_aggregate;
336       return nloc_aggregate;
337 
338     case DW_TAG_structure_type:
339     case DW_TAG_class_type:
340     case DW_TAG_union_type:
341     case DW_TAG_array_type:
342       if (dwarf_aggregate_size (typedie, &size) != 0)
343 	return -1;
344 
345       /* If this qualifies as an homogeneous floating-point aggregate
346 	 (HFA), then it should be returned in FP regs. */
347       int nfpreg = hfa_type (typedie, size, locp, 0);
348       if (nfpreg < 0)
349 	return nfpreg;
350       else if (nfpreg > 0 && nfpreg <= 8)
351 	return nfpreg == 1 ? nloc_fpreg : nloc_fpregs (nfpreg);
352 
353       if (size > 32)
354 	goto large;
355 
356       goto intreg;
357     }
358 
359   /* XXX We don't have a good way to return specific errors from ebl calls.
360      This value means we do not understand the type, but it is well-formed
361      DWARF and might be valid.  */
362   return -2;
363 }
364