1/* Function return value location for IA64 ABI.
2   Copyright (C) 2006, 2007 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.  */
38static 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
72DEFINE_FPREG (4);
73DEFINE_FPREG (8);
74DEFINE_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.  */
82static 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.  */
91static int
92hfa_type (Dwarf_Die *typedie, const Dwarf_Op **locp, int fpregs_used)
93{
94  /* Descend the type structure, counting elements and finding their types.
95     If we find a datum that's not an FP type (and not quad FP), punt.
96     If we find a datum that's not the same FP type as the first datum, punt.
97     If we count more than eight total homogeneous FP data, punt.  */
98
99  inline int hfa (const Dwarf_Op *loc, int nregs)
100    {
101      if (fpregs_used == 0)
102	*locp = loc;
103      else if (*locp != loc)
104	return 9;
105      return fpregs_used + nregs;
106    }
107
108  int tag = dwarf_tag (typedie);
109  switch (tag)
110    {
111      Dwarf_Attribute attr_mem;
112
113    case -1:
114      return -1;
115
116    case DW_TAG_base_type:;
117      int size = dwarf_bytesize (typedie);
118      if (size < 0)
119	return -1;
120
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		if (tag == DW_TAG_union_type)
182		  {
183		    int used = hfa_type (child_typedie, locp, fpregs_used);
184		    if (used < 0 || used > 8)
185		      return used;
186		    if (used > max_used)
187		      max_used = used;
188		  }
189		else
190		  {
191		    fpregs_used = hfa_type (child_typedie, locp, fpregs_used);
192		    if (fpregs_used < 0 || fpregs_used > 8)
193		      return fpregs_used;
194		  }
195	      }
196	  while (dwarf_siblingof (&child_mem, &child_mem) == 0);
197	  if (tag == DW_TAG_union_type)
198	    fpregs_used = max_used;
199	  break;
200	}
201      break;
202
203    case DW_TAG_array_type:;
204      size = dwarf_bytesize (typedie);
205      if (size < 0)
206	return 9;
207      if (size == 0)
208	break;
209
210      Dwarf_Die base_type_mem;
211      Dwarf_Die *base_typedie
212	= dwarf_formref_die (dwarf_attr_integrate (typedie, DW_AT_type,
213						   &attr_mem),
214			     &base_type_mem);
215
216      int used = hfa_type (base_typedie, locp, 0);
217      if (used < 0 || used > 8)
218	return used;
219      if (size % (*locp)[1].number != 0)
220	return 0;
221      size /= (*locp)[1].number;
222      fpregs_used += used * size;
223      break;
224
225    default:
226      return 9;
227    }
228
229  return fpregs_used;
230}
231
232int
233ia64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
234{
235  /* Start with the function's type, and get the DW_AT_type attribute,
236     which is the type of the return value.  */
237
238  Dwarf_Attribute attr_mem;
239  Dwarf_Attribute *attr = dwarf_attr_integrate (functypedie, DW_AT_type,
240						&attr_mem);
241  if (attr == NULL)
242    /* The function has no return value, like a `void' function in C.  */
243    return 0;
244
245  Dwarf_Die die_mem;
246  Dwarf_Die *typedie = dwarf_formref_die (attr, &die_mem);
247  int tag = dwarf_tag (typedie);
248
249  /* Follow typedefs and qualifiers to get to the actual type.  */
250  while (tag == DW_TAG_typedef
251	 || tag == DW_TAG_const_type || tag == DW_TAG_volatile_type
252	 || tag == DW_TAG_restrict_type || tag == DW_TAG_mutable_type)
253    {
254      attr = dwarf_attr (typedie, DW_AT_type, &attr_mem);
255      typedie = dwarf_formref_die (attr, &die_mem);
256      tag = dwarf_tag (typedie);
257    }
258
259  Dwarf_Word size;
260  switch (tag)
261    {
262    case -1:
263      return -1;
264
265    case DW_TAG_subrange_type:
266      if (! dwarf_hasattr_integrate (typedie, DW_AT_byte_size))
267	{
268	  attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
269	  typedie = dwarf_formref_die (attr, &die_mem);
270	  tag = dwarf_tag (typedie);
271	}
272      /* Fall through.  */
273
274    case DW_TAG_base_type:
275    case DW_TAG_enumeration_type:
276    case DW_TAG_pointer_type:
277    case DW_TAG_ptr_to_member_type:
278      if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_byte_size,
279						 &attr_mem), &size) != 0)
280	{
281	  if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
282	    size = 8;
283	  else
284	    return -1;
285	}
286      if (tag == DW_TAG_base_type)
287	{
288	  Dwarf_Word encoding;
289	  if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
290						     &attr_mem),
291			       &encoding) != 0)
292	    return -1;
293
294	  switch (encoding)
295	    {
296	    case DW_ATE_float:
297	      switch (size)
298		{
299		case 4:		/* float */
300		  *locp = loc_fpreg_4;
301		  return nloc_fpreg;
302		case 8:		/* double */
303		  *locp = loc_fpreg_8;
304		  return nloc_fpreg;
305		case 10:       /* x86-style long double, not really used */
306		  *locp = loc_fpreg_10;
307		  return nloc_fpreg;
308		case 16:	/* long double, IEEE quad format */
309		  *locp = loc_intreg;
310		  return nloc_intregs (2);
311		}
312	      return -2;
313
314	    case DW_ATE_complex_float:
315	      switch (size)
316		{
317		case 4 * 2:	/* complex float */
318		  *locp = loc_fpreg_4;
319		  return nloc_fpregs (2);
320		case 8 * 2:	/* complex double */
321		  *locp = loc_fpreg_8;
322		  return nloc_fpregs (2);
323		case 10 * 2:	/* complex long double (x86-style) */
324		  *locp = loc_fpreg_10;
325		  return nloc_fpregs (2);
326		case 16 * 2:	/* complex long double (IEEE quad) */
327		  *locp = loc_intreg;
328		  return nloc_intregs (4);
329		}
330	      return -2;
331	    }
332	}
333
334    intreg:
335      *locp = loc_intreg;
336      if (size <= 8)
337	return nloc_intreg;
338      if (size <= 32)
339	return nloc_intregs ((size + 7) / 8);
340
341    large:
342      *locp = loc_aggregate;
343      return nloc_aggregate;
344
345    case DW_TAG_structure_type:
346    case DW_TAG_class_type:
347    case DW_TAG_union_type:
348    case DW_TAG_array_type:
349      if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_byte_size,
350						 &attr_mem), &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, 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