Imported Upstream version 0.155
[platform/upstream/elfutils.git] / backends / ppc64_retval.c
1 /* Function return value location for Linux/PPC64 ABI.
2    Copyright (C) 2005-2010 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 ppc64_
37 #include "libebl_CPU.h"
38
39
40 /* r3.  */
41 static const Dwarf_Op loc_intreg[] =
42   {
43     { .atom = DW_OP_reg3 }
44   };
45 #define nloc_intreg     1
46
47 /* f1, or f1:f2, or f1:f4.  */
48 static const Dwarf_Op loc_fpreg[] =
49   {
50     { .atom = DW_OP_regx, .number = 33 }, { .atom = DW_OP_piece, .number = 8 },
51     { .atom = DW_OP_regx, .number = 34 }, { .atom = DW_OP_piece, .number = 8 },
52     { .atom = DW_OP_regx, .number = 35 }, { .atom = DW_OP_piece, .number = 8 },
53     { .atom = DW_OP_regx, .number = 36 }, { .atom = DW_OP_piece, .number = 8 },
54   };
55 #define nloc_fpreg      1
56 #define nloc_fp2regs    4
57 #define nloc_fp4regs    8
58
59 /* vr2.  */
60 static const Dwarf_Op loc_vmxreg[] =
61   {
62     { .atom = DW_OP_regx, .number = 1124 + 2 }
63   };
64 #define nloc_vmxreg     1
65
66 /* The return value is a structure and is actually stored in stack space
67    passed in a hidden argument by the caller.  But, the compiler
68    helpfully returns the address of that space in r3.  */
69 static const Dwarf_Op loc_aggregate[] =
70   {
71     { .atom = DW_OP_breg3, .number = 0 }
72   };
73 #define nloc_aggregate 1
74
75 int
76 ppc64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
77 {
78   /* Start with the function's type, and get the DW_AT_type attribute,
79      which is the type of the return value.  */
80
81   Dwarf_Attribute attr_mem;
82   Dwarf_Attribute *attr = dwarf_attr_integrate (functypedie, DW_AT_type,
83                                                 &attr_mem);
84   if (attr == NULL)
85     /* The function has no return value, like a `void' function in C.  */
86     return 0;
87
88   Dwarf_Die die_mem;
89   Dwarf_Die *typedie = dwarf_formref_die (attr, &die_mem);
90   int tag = dwarf_tag (typedie);
91
92   /* Follow typedefs and qualifiers to get to the actual type.  */
93   while (tag == DW_TAG_typedef
94          || tag == DW_TAG_const_type || tag == DW_TAG_volatile_type
95          || tag == DW_TAG_restrict_type || tag == DW_TAG_mutable_type)
96     {
97       attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
98       typedie = dwarf_formref_die (attr, &die_mem);
99       tag = dwarf_tag (typedie);
100     }
101
102   Dwarf_Word size;
103   switch (tag)
104     {
105     case -1:
106       return -1;
107
108     case DW_TAG_subrange_type:
109       if (! dwarf_hasattr_integrate (typedie, DW_AT_byte_size))
110         {
111           attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
112           typedie = dwarf_formref_die (attr, &die_mem);
113           tag = dwarf_tag (typedie);
114         }
115       /* Fall through.  */
116
117     case DW_TAG_base_type:
118     case DW_TAG_enumeration_type:
119     case DW_TAG_pointer_type:
120     case DW_TAG_ptr_to_member_type:
121       if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_byte_size,
122                                                  &attr_mem), &size) != 0)
123         {
124           if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
125             size = 8;
126           else
127             return -1;
128         }
129       if (tag == DW_TAG_base_type)
130         {
131           Dwarf_Word encoding;
132           if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
133                                                      &attr_mem),
134                                &encoding) != 0)
135             return -1;
136
137           if (encoding == DW_ATE_float || encoding == DW_ATE_complex_float)
138             {
139               *locp = loc_fpreg;
140               if (size <= 8)
141                 return nloc_fpreg;
142               if (size <= 16)
143                 return nloc_fp2regs;
144               if (size <= 32)
145                 return nloc_fp4regs;
146             }
147         }
148       if (size <= 8)
149         {
150         intreg:
151           *locp = loc_intreg;
152           return nloc_intreg;
153         }
154
155       /* Else fall through.  */
156     case DW_TAG_structure_type:
157     case DW_TAG_class_type:
158     case DW_TAG_union_type:
159     aggregate:
160       *locp = loc_aggregate;
161       return nloc_aggregate;
162
163     case DW_TAG_array_type:
164       {
165         bool is_vector;
166         if (dwarf_formflag (dwarf_attr_integrate (typedie, DW_AT_GNU_vector,
167                                                   &attr_mem), &is_vector) == 0
168             && is_vector)
169           {
170             *locp = loc_vmxreg;
171             return nloc_vmxreg;
172           }
173       }
174       /* Fall through.  */
175
176     case DW_TAG_string_type:
177       if (dwarf_aggregate_size (typedie, &size) == 0 && size <= 8)
178         {
179           if (tag == DW_TAG_array_type)
180             {
181               /* Check if it's a character array.  */
182               attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
183               typedie = dwarf_formref_die (attr, &die_mem);
184               tag = dwarf_tag (typedie);
185               if (tag != DW_TAG_base_type)
186                 goto aggregate;
187               if (dwarf_formudata (dwarf_attr_integrate (typedie,
188                                                          DW_AT_byte_size,
189                                                          &attr_mem),
190                                    &size) != 0)
191                 return -1;
192               if (size != 1)
193                 goto aggregate;
194             }
195           goto intreg;
196         }
197       goto aggregate;
198     }
199
200   /* XXX We don't have a good way to return specific errors from ebl calls.
201      This value means we do not understand the type, but it is well-formed
202      DWARF and might be valid.  */
203   return -2;
204 }