7645c3c89cb2567b2dc180d246fa9cedda8f29cc
[platform/upstream/elfutils.git] / backends / ia64_retval.c
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
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
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 }