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