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.  */
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, 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
233int
234ia64_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