Coverage Report

Created: 2025-08-03 07:07

/src/elfutils/backends/loongarch_retval.c
Line
Count
Source (jump to first uncovered line)
1
/* Function return value location for Linux/LoongArch ABI.
2
   Copyright (C) 2013 Red Hat, Inc.
3
   Copyright (C) 2023 OpenAnolis community LoongArch SIG.
4
   Copyright (C) 2023 Loongson Technology Corporation Limited.
5
6
   This file is part of elfutils.
7
8
   This file is free software; you can redistribute it and/or modify
9
   it under the terms of either
10
11
     * the GNU Lesser General Public License as published by the Free
12
       Software Foundation; either version 3 of the License, or (at
13
       your option) any later version
14
15
   or
16
17
     * the GNU General Public License as published by the Free
18
       Software Foundation; either version 2 of the License, or (at
19
       your option) any later version
20
21
   or both in parallel, as here.
22
23
   elfutils is distributed in the hope that it will be useful, but
24
   WITHOUT ANY WARRANTY; without even the implied warranty of
25
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26
   General Public License for more details.
27
28
   You should have received copies of the GNU General Public License and
29
   the GNU Lesser General Public License along with this program.  If
30
   not, see <http://www.gnu.org/licenses/>.  */
31
32
#ifdef HAVE_CONFIG_H
33
# include <config.h>
34
#endif
35
36
#include <stdio.h>
37
#include <inttypes.h>
38
39
#include <assert.h>
40
#include <dwarf.h>
41
42
#define BACKEND loongarch_
43
#include "libebl_CPU.h"
44
45
static int
46
dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep)
47
0
{
48
0
  int bits;
49
0
  if (((bits = 8 * dwarf_bytesize (die)) < 0
50
0
       && (bits = dwarf_bitsize (die)) < 0)
51
0
      || bits % 8 != 0)
52
0
    return -1;
53
54
0
  *sizep = bits / 8;
55
0
  return 0;
56
0
}
57
58
static int
59
pass_in_gpr (const Dwarf_Op **locp, Dwarf_Word size)
60
0
{
61
0
  static const Dwarf_Op loc[] =
62
0
    {
63
0
      { .atom = DW_OP_reg4 }, { .atom = DW_OP_piece, .number = 8 },
64
0
      { .atom = DW_OP_reg5 }, { .atom = DW_OP_piece, .number = 8 }
65
0
    };
66
67
0
  *locp = loc;
68
0
  return size <= 8 ? 1 : 4;
69
0
}
70
71
static int
72
pass_by_ref (const Dwarf_Op **locp)
73
0
{
74
0
  static const Dwarf_Op loc[] = { { .atom = DW_OP_breg4 } };
75
76
0
  *locp = loc;
77
0
  return 1;
78
0
}
79
80
static int
81
pass_in_fpr (const Dwarf_Op **locp, Dwarf_Word size)
82
0
{
83
0
  static const Dwarf_Op loc[] =
84
0
    {
85
0
      { .atom = DW_OP_regx, .number = 32 },
86
0
      { .atom = DW_OP_piece, .number = 8 },
87
0
      { .atom = DW_OP_regx, .number = 33 },
88
0
      { .atom = DW_OP_piece, .number = 8 }
89
0
    };
90
91
0
  *locp = loc;
92
0
  return size <= 8 ? 1 : 4;
93
0
}
94
95
int
96
loongarch_return_value_location(Dwarf_Die *functypedie,
97
                                const Dwarf_Op **locp)
98
0
{
99
  /* Start with the function's type, and get the DW_AT_type attribute,
100
     which is the type of the return value.  */
101
0
  Dwarf_Die typedie;
102
0
  int tag = dwarf_peeled_die_type (functypedie, &typedie);
103
0
  if (tag <= 0)
104
0
    return tag;
105
106
0
  Dwarf_Word size = (Dwarf_Word)-1;
107
108
  /* If the argument type is a Composite Type that is larger than 16
109
     bytes, then the argument is copied to memory allocated by the
110
     caller and the argument is replaced by a pointer to the copy.  */
111
0
  if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type
112
0
      || tag == DW_TAG_class_type || tag == DW_TAG_array_type)
113
0
    {
114
0
      if (dwarf_aggregate_size (&typedie, &size) < 0)
115
0
  return -1;
116
117
      /* Aggregates larger than 2*GRLEN bits are passed by reference.  */
118
0
      if (size > 16)
119
0
  return pass_by_ref (locp);
120
      /* Aggregates whose total size is no more than GRLEN bits are passed in
121
   a register.  Aggregates whose total size is no more than 2*GRLEN bits
122
   are passed in a pair of registers.  */
123
0
      else
124
0
  return pass_in_gpr (locp, size);
125
0
    }
126
127
0
  if (tag == DW_TAG_base_type || dwarf_is_pointer (tag))
128
0
    {
129
0
      if (dwarf_bytesize_aux (&typedie, &size) < 0)
130
0
  {
131
0
    if (dwarf_is_pointer (tag))
132
0
      size = 8;
133
0
    else
134
0
      return -1;
135
0
  }
136
137
0
      Dwarf_Attribute attr_mem;
138
0
      if (tag == DW_TAG_base_type)
139
0
  {
140
0
    Dwarf_Word encoding;
141
0
    if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding,
142
0
                 &attr_mem),
143
0
             &encoding) != 0)
144
0
      return -1;
145
146
0
    switch (encoding)
147
0
      {
148
0
      case DW_ATE_boolean:
149
0
      case DW_ATE_signed:
150
0
      case DW_ATE_unsigned:
151
0
      case DW_ATE_unsigned_char:
152
0
      case DW_ATE_signed_char:
153
        /* Scalars that are at most GRLEN bits wide are passed in a single
154
     argument register.  Scalars that are 2*GRLEN bits wide are
155
     passed in a pair of argument registers.  Scalars wider than
156
     2*GRLEN are passed by reference.  */
157
0
        return pass_in_gpr (locp, size);
158
159
0
      case DW_ATE_float:
160
        /* A real floating-point argument is passed in a floating-point
161
     argument register if it is no more than FLEN bits wide,
162
     otherwise it is passed according to the integer calling
163
     convention.  */
164
0
        switch (size)
165
0
    {
166
0
    case 4: /* single */
167
0
                case 8: /* double */
168
0
                  return pass_in_fpr (locp, size);
169
170
0
                case 16: /* quad */
171
0
            return pass_in_gpr (locp, size);
172
173
0
    default:
174
0
      return -2;
175
0
    }
176
177
0
      case DW_ATE_complex_float:
178
        /* A complex floating-point number is passed as though it were a
179
     struct containing two floating-point reals.  */
180
0
        switch (size)
181
0
    {
182
0
    case 8: /* float _Complex */
183
0
                case 16: /* double _Complex */
184
0
                  return pass_in_fpr (locp, size);
185
186
0
                case 32: /* long double _Complex */
187
0
      return pass_by_ref (locp);
188
189
0
    default:
190
0
      return -2;
191
0
    }
192
0
      }
193
194
0
    return -2;
195
0
  }
196
0
      else
197
0
  return pass_in_gpr (locp, size);
198
0
    }
199
200
0
  *locp = NULL;
201
0
  return 0;
202
0
}