1/* Function return value location for Linux/AArch64 ABI. 2 Copyright (C) 2013 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 <stdio.h> 34#include <inttypes.h> 35 36#include <assert.h> 37#include <dwarf.h> 38 39#define BACKEND aarch64_ 40#include "libebl_CPU.h" 41 42static int 43skip_until (Dwarf_Die *child, int tag) 44{ 45 int i; 46 while (DWARF_TAG_OR_RETURN (child) != tag) 47 if ((i = dwarf_siblingof (child, child)) != 0) 48 /* If there are no members, then this is not a HFA. Errors 49 are propagated. */ 50 return i; 51 return 0; 52} 53 54static int 55dwarf_bytesize_aux (Dwarf_Die *die, Dwarf_Word *sizep) 56{ 57 int bits; 58 if (((bits = 8 * dwarf_bytesize (die)) < 0 59 && (bits = dwarf_bitsize (die)) < 0) 60 || bits % 8 != 0) 61 return -1; 62 63 *sizep = bits / 8; 64 return 0; 65} 66 67/* HFA (Homogeneous Floating-point Aggregate) is an aggregate type 68 whose members are all of the same floating-point type, which is 69 then base type of this HFA. Instead of being floating-point types 70 directly, members can instead themselves be HFA. Such HFA fields 71 are handled as if their type were HFA base type. 72 73 This function returns 0 if TYPEDIE is HFA, 1 if it is not, or -1 if 74 there were errors. In the former case, *SIZEP contains byte size 75 of the base type (e.g. 8 for IEEE double). *COUNT is set to the 76 number of leaf members of the HFA. */ 77static int hfa_type (Dwarf_Die *ftypedie, int tag, 78 Dwarf_Word *sizep, Dwarf_Word *countp); 79 80/* Return 0 if MEMBDIE refers to a member with a floating-point or HFA 81 type, or 1 if it's not. Return -1 for errors. The meaning of the 82 remaining arguments is as documented at hfa_type. */ 83static int 84member_is_fp (Dwarf_Die *membdie, Dwarf_Word *sizep, Dwarf_Word *countp) 85{ 86 Dwarf_Die typedie; 87 int tag = dwarf_peeled_die_type (membdie, &typedie); 88 switch (tag) 89 { 90 case DW_TAG_base_type:; 91 Dwarf_Word encoding; 92 Dwarf_Attribute attr_mem; 93 if (dwarf_attr_integrate (&typedie, DW_AT_encoding, &attr_mem) == NULL 94 || dwarf_formudata (&attr_mem, &encoding) != 0) 95 return -1; 96 97 switch (encoding) 98 { 99 case DW_ATE_complex_float: 100 *countp = 2; 101 break; 102 103 case DW_ATE_float: 104 *countp = 1; 105 break; 106 107 default: 108 return 1; 109 } 110 111 if (dwarf_bytesize_aux (&typedie, sizep) < 0) 112 return -1; 113 114 *sizep /= *countp; 115 return 0; 116 117 case DW_TAG_structure_type: 118 case DW_TAG_union_type: 119 case DW_TAG_array_type: 120 return hfa_type (&typedie, tag, sizep, countp); 121 } 122 123 return 1; 124} 125 126static int 127hfa_type (Dwarf_Die *ftypedie, int tag, Dwarf_Word *sizep, Dwarf_Word *countp) 128{ 129 assert (tag == DW_TAG_structure_type || tag == DW_TAG_class_type 130 || tag == DW_TAG_union_type || tag == DW_TAG_array_type); 131 132 int i; 133 if (tag == DW_TAG_array_type) 134 { 135 Dwarf_Word tot_size; 136 if (dwarf_aggregate_size (ftypedie, &tot_size) < 0) 137 return -1; 138 139 /* For vector types, we don't care about the underlying 140 type, but only about the vector type itself. */ 141 bool vec; 142 Dwarf_Attribute attr_mem; 143 if (dwarf_formflag (dwarf_attr_integrate (ftypedie, DW_AT_GNU_vector, 144 &attr_mem), &vec) == 0 145 && vec) 146 { 147 *sizep = tot_size; 148 *countp = 1; 149 150 return 0; 151 } 152 153 if ((i = member_is_fp (ftypedie, sizep, countp)) == 0) 154 { 155 *countp = tot_size / *sizep; 156 return 0; 157 } 158 159 return i; 160 } 161 162 /* Find first DW_TAG_member and determine its type. */ 163 Dwarf_Die member; 164 if ((i = dwarf_child (ftypedie, &member) != 0)) 165 return i; 166 167 if ((i = skip_until (&member, DW_TAG_member)) != 0) 168 return i; 169 170 *countp = 0; 171 if ((i = member_is_fp (&member, sizep, countp)) != 0) 172 return i; 173 174 while ((i = dwarf_siblingof (&member, &member)) == 0 175 && (i = skip_until (&member, DW_TAG_member)) == 0) 176 { 177 Dwarf_Word size, count; 178 if ((i = member_is_fp (&member, &size, &count)) != 0) 179 return i; 180 181 if (*sizep != size) 182 return 1; 183 184 *countp += count; 185 } 186 187 /* At this point we already have at least one FP member, which means 188 FTYPEDIE is an HFA. So either return 0, or propagate error. */ 189 return i < 0 ? i : 0; 190} 191 192static int 193pass_in_gpr (const Dwarf_Op **locp, Dwarf_Word size) 194{ 195 static const Dwarf_Op loc[] = 196 { 197 { .atom = DW_OP_reg0 }, { .atom = DW_OP_piece, .number = 8 }, 198 { .atom = DW_OP_reg1 }, { .atom = DW_OP_piece, .number = 8 } 199 }; 200 201 *locp = loc; 202 return size <= 8 ? 1 : 4; 203} 204 205static int 206pass_by_ref (const Dwarf_Op **locp) 207{ 208 static const Dwarf_Op loc[] = { { .atom = DW_OP_breg0 } }; 209 210 *locp = loc; 211 return 1; 212} 213 214static int 215pass_hfa (const Dwarf_Op **locp, Dwarf_Word size, Dwarf_Word count) 216{ 217 assert (count >= 1 && count <= 4); 218 assert (size == 2 || size == 4 || size == 8 || size == 16); 219 220#define DEFINE_FPREG(NAME, SIZE) \ 221 static const Dwarf_Op NAME[] = { \ 222 { .atom = DW_OP_regx, .number = 64 }, \ 223 { .atom = DW_OP_piece, .number = SIZE }, \ 224 { .atom = DW_OP_regx, .number = 65 }, \ 225 { .atom = DW_OP_piece, .number = SIZE }, \ 226 { .atom = DW_OP_regx, .number = 66 }, \ 227 { .atom = DW_OP_piece, .number = SIZE }, \ 228 { .atom = DW_OP_regx, .number = 67 }, \ 229 { .atom = DW_OP_piece, .number = SIZE } \ 230 } 231 232 switch (size) 233 { 234 case 2:; 235 DEFINE_FPREG (loc_hfa_2, 2); 236 *locp = loc_hfa_2; 237 break; 238 239 case 4:; 240 DEFINE_FPREG (loc_hfa_4, 4); 241 *locp = loc_hfa_4; 242 break; 243 244 case 8:; 245 DEFINE_FPREG (loc_hfa_8, 8); 246 *locp = loc_hfa_8; 247 break; 248 249 case 16:; 250 DEFINE_FPREG (loc_hfa_16, 16); 251 *locp = loc_hfa_16; 252 break; 253 } 254#undef DEFINE_FPREG 255 256 return count == 1 ? 1 : 2 * count; 257} 258 259static int 260pass_in_simd (const Dwarf_Op **locp) 261{ 262 /* This is like passing single-element HFA. Size doesn't matter, so 263 pretend it's for example double. */ 264 return pass_hfa (locp, 8, 1); 265} 266 267int 268aarch64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp) 269{ 270 /* Start with the function's type, and get the DW_AT_type attribute, 271 which is the type of the return value. */ 272 Dwarf_Die typedie; 273 int tag = dwarf_peeled_die_type (functypedie, &typedie); 274 if (tag <= 0) 275 return tag; 276 277 Dwarf_Word size = (Dwarf_Word)-1; 278 279 /* If the argument type is a Composite Type that is larger than 16 280 bytes, then the argument is copied to memory allocated by the 281 caller and the argument is replaced by a pointer to the copy. */ 282 if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type 283 || tag == DW_TAG_class_type || tag == DW_TAG_array_type) 284 { 285 Dwarf_Word base_size, count; 286 switch (hfa_type (&typedie, tag, &base_size, &count)) 287 { 288 default: 289 return -1; 290 291 case 0: 292 assert (count > 0); 293 if (count <= 4) 294 return pass_hfa (locp, base_size, count); 295 /* Fall through. */ 296 297 case 1: 298 /* Not a HFA. */ 299 if (dwarf_aggregate_size (&typedie, &size) < 0) 300 return -1; 301 if (size > 16) 302 return pass_by_ref (locp); 303 } 304 } 305 306 if (tag == DW_TAG_base_type 307 || tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type) 308 { 309 if (dwarf_bytesize_aux (&typedie, &size) < 0) 310 { 311 if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type) 312 size = 8; 313 else 314 return -1; 315 } 316 317 Dwarf_Attribute attr_mem; 318 if (tag == DW_TAG_base_type) 319 { 320 Dwarf_Word encoding; 321 if (dwarf_formudata (dwarf_attr_integrate (&typedie, DW_AT_encoding, 322 &attr_mem), 323 &encoding) != 0) 324 return -1; 325 326 switch (encoding) 327 { 328 /* If the argument is a Half-, Single-, Double- or Quad- 329 precision Floating-point [...] the argument is allocated 330 to the least significant bits of register v[NSRN]. */ 331 case DW_ATE_float: 332 switch (size) 333 { 334 case 2: /* half */ 335 case 4: /* sigle */ 336 case 8: /* double */ 337 case 16: /* quad */ 338 return pass_in_simd (locp); 339 340 default: 341 return -2; 342 } 343 344 case DW_ATE_complex_float: 345 switch (size) 346 { 347 case 8: /* float _Complex */ 348 case 16: /* double _Complex */ 349 case 32: /* long double _Complex */ 350 return pass_hfa (locp, size / 2, 2); 351 352 default: 353 return -2; 354 } 355 356 /* If the argument is an Integral or Pointer Type, the 357 size of the argument is less than or equal to 8 bytes 358 [...] the argument is copied to the least significant 359 bits in x[NGRN]. */ 360 case DW_ATE_boolean: 361 case DW_ATE_signed: 362 case DW_ATE_unsigned: 363 case DW_ATE_unsigned_char: 364 case DW_ATE_signed_char: 365 return pass_in_gpr (locp, size); 366 } 367 368 return -2; 369 } 370 else 371 return pass_in_gpr (locp, size); 372 } 373 374 *locp = NULL; 375 return 0; 376} 377