Coverage Report

Created: 2025-07-11 06:46

/src/elfutils/backends/ia64_retval.c
Line
Count
Source (jump to first uncovered line)
1
/* Function return value location for IA64 ABI.
2
   Copyright (C) 2006-2010, 2014 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 ia64_
37
#include "libebl_CPU.h"
38
39
40
/* r8, or pair r8, r9, or aggregate up to r8-r11.  */
41
static const Dwarf_Op loc_intreg[] =
42
  {
43
    { .atom = DW_OP_reg8 }, { .atom = DW_OP_piece, .number = 8 },
44
    { .atom = DW_OP_reg9 }, { .atom = DW_OP_piece, .number = 8 },
45
    { .atom = DW_OP_reg10 }, { .atom = DW_OP_piece, .number = 8 },
46
    { .atom = DW_OP_reg11 }, { .atom = DW_OP_piece, .number = 8 },
47
  };
48
0
#define nloc_intreg 1
49
0
#define nloc_intregs(n) (2 * (n))
50
51
/* f8, or aggregate up to f8-f15.  */
52
#define DEFINE_FPREG(size)                  \
53
  static const Dwarf_Op loc_fpreg_##size[] =              \
54
    {                       \
55
      { .atom = DW_OP_regx, .number = 128 + 8 },            \
56
      { .atom = DW_OP_piece, .number = size },              \
57
      { .atom = DW_OP_regx, .number = 128 + 9 },            \
58
      { .atom = DW_OP_piece, .number = size },              \
59
      { .atom = DW_OP_regx, .number = 128 + 10 },           \
60
      { .atom = DW_OP_piece, .number = size },              \
61
      { .atom = DW_OP_regx, .number = 128 + 11 },           \
62
      { .atom = DW_OP_piece, .number = size },              \
63
      { .atom = DW_OP_regx, .number = 128 + 12 },           \
64
      { .atom = DW_OP_piece, .number = size },              \
65
      { .atom = DW_OP_regx, .number = 128 + 13 },           \
66
      { .atom = DW_OP_piece, .number = size },              \
67
      { .atom = DW_OP_regx, .number = 128 + 14 },           \
68
      { .atom = DW_OP_piece, .number = size },              \
69
      { .atom = DW_OP_regx, .number = 128 + 15 },           \
70
      { .atom = DW_OP_piece, .number = size },              \
71
    }
72
0
#define nloc_fpreg  1
73
0
#define nloc_fpregs(n)  (2 * (n))
74
75
DEFINE_FPREG (4);
76
DEFINE_FPREG (8);
77
DEFINE_FPREG (10);
78
79
#undef DEFINE_FPREG
80
81
82
/* The return value is a structure and is actually stored in stack space
83
   passed in a hidden argument by the caller.  But, the compiler
84
   helpfully returns the address of that space in r8.  */
85
static const Dwarf_Op loc_aggregate[] =
86
  {
87
    { .atom = DW_OP_breg8, .number = 0 }
88
  };
89
0
#define nloc_aggregate 1
90
91
92
static inline int
93
compute_hfa (const Dwarf_Op *loc, int nregs,
94
       const Dwarf_Op **locp, int fpregs_used)
95
0
{
96
0
  if (fpregs_used == 0)
97
0
    *locp = loc;
98
0
  else if (*locp != loc)
99
0
    return 9;
100
0
  return fpregs_used + nregs;
101
0
}
102
103
/* If this type is an HFA small enough to be returned in FP registers,
104
   return the number of registers to use.  Otherwise 9, or -1 for errors.  */
105
static int
106
hfa_type (Dwarf_Die *typedie, Dwarf_Word size,
107
    const Dwarf_Op **locp, int fpregs_used)
108
0
{
109
  /* Descend the type structure, counting elements and finding their types.
110
     If we find a datum that's not an FP type (and not quad FP), punt.
111
     If we find a datum that's not the same FP type as the first datum, punt.
112
     If we count more than eight total homogeneous FP data, punt.  */
113
114
0
  int tag = DWARF_TAG_OR_RETURN (typedie);
115
0
  switch (tag)
116
0
    {
117
0
      Dwarf_Attribute attr_mem;
118
119
0
    case -1:
120
0
      return -1;
121
122
0
    case DW_TAG_base_type:;
123
0
      Dwarf_Word encoding;
124
0
      if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
125
0
             &attr_mem), &encoding) != 0)
126
0
  return -1;
127
128
0
#define hfa(loc, nregs) compute_hfa(loc, nregs, locp, fpregs_used)
129
0
      switch (encoding)
130
0
  {
131
0
  case DW_ATE_float:
132
0
    switch (size)
133
0
      {
134
0
      case 4:   /* float */
135
0
        return hfa (loc_fpreg_4, 1);
136
0
      case 8:   /* double */
137
0
        return hfa (loc_fpreg_8, 1);
138
0
      case 10:       /* x86-style long double, not really used */
139
0
        return hfa (loc_fpreg_10, 1);
140
0
      }
141
0
    break;
142
143
0
  case DW_ATE_complex_float:
144
0
    switch (size)
145
0
      {
146
0
      case 4 * 2: /* complex float */
147
0
        return hfa (loc_fpreg_4, 2);
148
0
      case 8 * 2: /* complex double */
149
0
        return hfa (loc_fpreg_8, 2);
150
0
      case 10 * 2:  /* complex long double (x86-style) */
151
0
        return hfa (loc_fpreg_10, 2);
152
0
      }
153
0
    break;
154
0
  }
155
0
      break;
156
157
0
    case DW_TAG_structure_type:
158
0
    case DW_TAG_class_type:
159
0
    case DW_TAG_union_type:;
160
0
      Dwarf_Die child_mem;
161
0
      switch (dwarf_child (typedie, &child_mem))
162
0
  {
163
0
  default:
164
0
    return -1;
165
166
0
  case 1:     /* No children: empty struct.  */
167
0
    break;
168
169
0
  case 0:;    /* Look at each element.  */
170
0
    int max_used = fpregs_used;
171
0
    do
172
0
      switch (dwarf_tag (&child_mem))
173
0
        {
174
0
        case -1:
175
0
    return -1;
176
177
0
        case DW_TAG_member:;
178
0
    Dwarf_Die child_type_mem;
179
0
    Dwarf_Die *child_typedie
180
0
      = dwarf_formref_die (dwarf_attr_integrate (&child_mem,
181
0
                   DW_AT_type,
182
0
                   &attr_mem),
183
0
               &child_type_mem);
184
0
    Dwarf_Word child_size;
185
0
    if (dwarf_aggregate_size (child_typedie, &child_size) != 0)
186
0
      return -1;
187
0
    if (tag == DW_TAG_union_type)
188
0
      {
189
0
        int used = hfa_type (child_typedie, child_size,
190
0
           locp, fpregs_used);
191
0
        if (used < 0 || used > 8)
192
0
          return used;
193
0
        if (used > max_used)
194
0
          max_used = used;
195
0
      }
196
0
    else
197
0
      {
198
0
        fpregs_used = hfa_type (child_typedie, child_size,
199
0
              locp, fpregs_used);
200
0
        if (fpregs_used < 0 || fpregs_used > 8)
201
0
          return fpregs_used;
202
0
      }
203
0
        }
204
0
    while (dwarf_siblingof (&child_mem, &child_mem) == 0);
205
0
    if (tag == DW_TAG_union_type)
206
0
      fpregs_used = max_used;
207
0
    break;
208
0
  }
209
0
      break;
210
211
0
    case DW_TAG_array_type:
212
0
      if (size == 0)
213
0
  break;
214
215
0
      Dwarf_Die base_type_mem;
216
0
      Dwarf_Die *base_typedie
217
0
  = dwarf_formref_die (dwarf_attr_integrate (typedie, DW_AT_type,
218
0
               &attr_mem),
219
0
           &base_type_mem);
220
0
      Dwarf_Word base_size;
221
0
      if (dwarf_aggregate_size (base_typedie, &base_size) != 0)
222
0
  return -1;
223
224
0
      int used = hfa_type (base_typedie, base_size, locp, 0);
225
0
      if (used < 0 || used > 8)
226
0
  return used;
227
0
      if (size % (*locp)[1].number != 0)
228
0
  return 0;
229
0
      fpregs_used += used * (size / (*locp)[1].number);
230
0
      break;
231
232
0
    default:
233
0
      return 9;
234
0
    }
235
236
0
  return fpregs_used;
237
0
}
238
239
int
240
ia64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
241
0
{
242
  /* Start with the function's type, and get the DW_AT_type attribute,
243
     which is the type of the return value.  */
244
0
  Dwarf_Die die_mem, *typedie = &die_mem;
245
0
  int tag = dwarf_peeled_die_type (functypedie, typedie);
246
0
  if (tag <= 0)
247
0
    return tag;
248
249
0
  Dwarf_Word size;
250
0
  switch (tag)
251
0
    {
252
0
    case -1:
253
0
      return -1;
254
255
0
    case DW_TAG_subrange_type:
256
0
      if (! dwarf_hasattr_integrate (typedie, DW_AT_byte_size))
257
0
  {
258
0
    Dwarf_Attribute attr_mem, *attr;
259
0
    attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
260
0
    typedie = dwarf_formref_die (attr, &die_mem);
261
0
    tag = DWARF_TAG_OR_RETURN (typedie);
262
0
  }
263
0
      FALLTHROUGH;
264
265
0
    case DW_TAG_base_type:
266
0
    case DW_TAG_enumeration_type:
267
0
    CASE_POINTER:
268
0
      {
269
0
  Dwarf_Attribute attr_mem;
270
0
  if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_byte_size,
271
0
               &attr_mem), &size) != 0)
272
0
    {
273
0
      if (dwarf_is_pointer (tag))
274
0
        size = 8;
275
0
      else
276
0
        return -1;
277
0
    }
278
0
      }
279
280
0
      if (tag == DW_TAG_base_type)
281
0
  {
282
0
    Dwarf_Attribute attr_mem;
283
0
    Dwarf_Word encoding;
284
0
    if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
285
0
                 &attr_mem),
286
0
             &encoding) != 0)
287
0
      return -1;
288
289
0
    switch (encoding)
290
0
      {
291
0
      case DW_ATE_float:
292
0
        switch (size)
293
0
    {
294
0
    case 4:   /* float */
295
0
      *locp = loc_fpreg_4;
296
0
      return nloc_fpreg;
297
0
    case 8:   /* double */
298
0
      *locp = loc_fpreg_8;
299
0
      return nloc_fpreg;
300
0
    case 10:       /* x86-style long double, not really used */
301
0
      *locp = loc_fpreg_10;
302
0
      return nloc_fpreg;
303
0
    case 16:  /* long double, IEEE quad format */
304
0
      *locp = loc_intreg;
305
0
      return nloc_intregs (2);
306
0
    }
307
0
        return -2;
308
309
0
      case DW_ATE_complex_float:
310
0
        switch (size)
311
0
    {
312
0
    case 4 * 2: /* complex float */
313
0
      *locp = loc_fpreg_4;
314
0
      return nloc_fpregs (2);
315
0
    case 8 * 2: /* complex double */
316
0
      *locp = loc_fpreg_8;
317
0
      return nloc_fpregs (2);
318
0
    case 10 * 2:  /* complex long double (x86-style) */
319
0
      *locp = loc_fpreg_10;
320
0
      return nloc_fpregs (2);
321
0
    case 16 * 2:  /* complex long double (IEEE quad) */
322
0
      *locp = loc_intreg;
323
0
      return nloc_intregs (4);
324
0
    }
325
0
        return -2;
326
0
      }
327
0
  }
328
329
0
    intreg:
330
0
      *locp = loc_intreg;
331
0
      if (size <= 8)
332
0
  return nloc_intreg;
333
0
      if (size <= 32)
334
0
  return nloc_intregs ((size + 7) / 8);
335
336
0
    large:
337
0
      *locp = loc_aggregate;
338
0
      return nloc_aggregate;
339
340
0
    case DW_TAG_structure_type:
341
0
    case DW_TAG_class_type:
342
0
    case DW_TAG_union_type:
343
0
    case DW_TAG_array_type:
344
0
      if (dwarf_aggregate_size (typedie, &size) != 0)
345
0
  return -1;
346
347
      /* If this qualifies as an homogeneous floating-point aggregate
348
   (HFA), then it should be returned in FP regs. */
349
0
      int nfpreg = hfa_type (typedie, size, locp, 0);
350
0
      if (nfpreg < 0)
351
0
  return nfpreg;
352
0
      else if (nfpreg > 0 && nfpreg <= 8)
353
0
  return nfpreg == 1 ? nloc_fpreg : nloc_fpregs (nfpreg);
354
355
0
      if (size > 32)
356
0
  goto large;
357
358
0
      goto intreg;
359
0
    }
360
361
  /* XXX We don't have a good way to return specific errors from ebl calls.
362
     This value means we do not understand the type, but it is well-formed
363
     DWARF and might be valid.  */
364
0
  return -2;
365
0
}