1 /** 2 * Break down a D type into basic (register) types for the Itanium C++ ABI. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes.d, _argtypes.d) 8 * Documentation: https://dlang.org/phobos/dmd_argtypes.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes.d 10 */ 11 12 module dmd.argtypes; 13 14 import core.stdc.stdio; 15 import core.checkedint; 16 17 import dmd.declaration; 18 import dmd.globals; 19 import dmd.mtype; 20 import dmd.visitor; 21 22 private bool isDMDx64Target() 23 { 24 version (MARS) 25 return global.params.is64bit; 26 else 27 return false; 28 } 29 30 /**************************************************** 31 * This breaks a type down into 'simpler' types that can be passed to a function 32 * in registers, and returned in registers. 33 * It's highly platform dependent. 34 * Params: 35 * t = type to break down 36 * Returns: 37 * tuple of types, each element can be passed in a register. 38 * A tuple of zero length means the type cannot be passed/returned in registers. 39 * null indicates a `void`. 40 * References: 41 * For 64 bit code, follows Itanium C++ ABI 1.86 Chapter 3 42 * http://refspecs.linux-foundation.org/cxxabi-1.86.html#calls 43 */ 44 extern (C++) TypeTuple toArgTypes(Type t) 45 { 46 extern (C++) final class ToArgTypes : Visitor 47 { 48 alias visit = Visitor.visit; 49 public: 50 TypeTuple result; 51 52 /***** 53 * Pass type in memory (i.e. on the stack), a tuple of one type, or a tuple of 2 types 54 */ 55 void memory() 56 { 57 //printf("\ttoArgTypes() %s => [ ]\n", t.toChars()); 58 result = new TypeTuple(); // pass on the stack 59 } 60 61 /// 62 void oneType(Type t) 63 { 64 result = new TypeTuple(t); 65 } 66 67 /// 68 void twoTypes(Type t1, Type t2) 69 { 70 result = new TypeTuple(t1, t2); 71 } 72 73 74 override void visit(Type) 75 { 76 // not valid for a parameter 77 } 78 79 override void visit(TypeError) 80 { 81 result = new TypeTuple(Type.terror); 82 } 83 84 override void visit(TypeBasic t) 85 { 86 Type t1 = null; 87 Type t2 = null; 88 switch (t.ty) 89 { 90 case Tvoid: 91 return; 92 case Tbool: 93 case Tint8: 94 case Tuns8: 95 case Tint16: 96 case Tuns16: 97 case Tint32: 98 case Tuns32: 99 case Tfloat32: 100 case Tint64: 101 case Tuns64: 102 case Tint128: 103 case Tuns128: 104 case Tfloat64: 105 case Tfloat80: 106 t1 = t; 107 break; 108 case Timaginary32: 109 t1 = Type.tfloat32; 110 break; 111 case Timaginary64: 112 t1 = Type.tfloat64; 113 break; 114 case Timaginary80: 115 t1 = Type.tfloat80; 116 break; 117 case Tcomplex32: 118 if (isDMDx64Target()) 119 t1 = Type.tfloat64; 120 else 121 { 122 t1 = Type.tfloat64; 123 t2 = Type.tfloat64; 124 } 125 break; 126 case Tcomplex64: 127 t1 = Type.tfloat64; 128 t2 = Type.tfloat64; 129 break; 130 case Tcomplex80: 131 t1 = Type.tfloat80; 132 t2 = Type.tfloat80; 133 break; 134 case Tchar: 135 t1 = Type.tuns8; 136 break; 137 case Twchar: 138 t1 = Type.tuns16; 139 break; 140 case Tdchar: 141 t1 = Type.tuns32; 142 break; 143 default: 144 assert(0); 145 } 146 if (t1) 147 { 148 if (t2) 149 return twoTypes(t1, t2); 150 else 151 return oneType(t1); 152 } 153 else 154 return memory(); 155 } 156 157 override void visit(TypeVector t) 158 { 159 return oneType(t); 160 } 161 162 override void visit(TypeAArray) 163 { 164 return oneType(Type.tvoidptr); 165 } 166 167 override void visit(TypePointer) 168 { 169 return oneType(Type.tvoidptr); 170 } 171 172 /************************************* 173 * Convert a floating point type into the equivalent integral type. 174 */ 175 static Type mergeFloatToInt(Type t) 176 { 177 switch (t.ty) 178 { 179 case Tfloat32: 180 case Timaginary32: 181 t = Type.tint32; 182 break; 183 case Tfloat64: 184 case Timaginary64: 185 case Tcomplex32: 186 t = Type.tint64; 187 break; 188 default: 189 debug 190 { 191 printf("mergeFloatToInt() %s\n", t.toChars()); 192 } 193 assert(0); 194 } 195 return t; 196 } 197 198 /************************************* 199 * This merges two types into an 8byte type. 200 * Params: 201 * t1 = first type (can be null) 202 * t2 = second type (can be null) 203 * offset2 = offset of t2 from start of t1 204 * Returns: 205 * type that encompasses both t1 and t2, null if cannot be done 206 */ 207 static Type argtypemerge(Type t1, Type t2, uint offset2) 208 { 209 //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1.toChars() : "", t2 ? t2.toChars() : "", offset2); 210 if (!t1) 211 { 212 assert(!t2 || offset2 == 0); 213 return t2; 214 } 215 if (!t2) 216 return t1; 217 const sz1 = t1.size(Loc.initial); 218 const sz2 = t2.size(Loc.initial); 219 assert(sz1 != SIZE_INVALID && sz2 != SIZE_INVALID); 220 if (t1.ty != t2.ty && (t1.ty == Tfloat80 || t2.ty == Tfloat80)) 221 return null; 222 // [float,float] => [cfloat] 223 if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4) 224 return Type.tfloat64; 225 // Merging floating and non-floating types produces the non-floating type 226 if (t1.isfloating()) 227 { 228 if (!t2.isfloating()) 229 t1 = mergeFloatToInt(t1); 230 } 231 else if (t2.isfloating()) 232 t2 = mergeFloatToInt(t2); 233 Type t; 234 // Pick type with larger size 235 if (sz1 < sz2) 236 t = t2; 237 else 238 t = t1; 239 // If t2 does not lie within t1, need to increase the size of t to enclose both 240 bool overflow; 241 const offset3 = addu(offset2, sz2, overflow); 242 assert(!overflow); 243 if (offset2 && sz1 < offset3) 244 { 245 switch (offset3) 246 { 247 case 2: 248 t = Type.tint16; 249 break; 250 case 3: 251 case 4: 252 t = Type.tint32; 253 break; 254 default: 255 t = Type.tint64; 256 break; 257 } 258 } 259 return t; 260 } 261 262 override void visit(TypeDArray) 263 { 264 /* Should be done as if it were: 265 * struct S { size_t length; void* ptr; } 266 */ 267 if (isDMDx64Target() && !global.params.isLP64) 268 { 269 // For AMD64 ILP32 ABI, D arrays fit into a single integer register. 270 const offset = cast(uint)Type.tsize_t.size(Loc.initial); 271 Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset); 272 if (t) 273 { 274 return oneType(t); 275 } 276 } 277 return twoTypes(Type.tsize_t, Type.tvoidptr); 278 } 279 280 override void visit(TypeDelegate) 281 { 282 /* Should be done as if it were: 283 * struct S { void* funcptr; void* ptr; } 284 */ 285 if (isDMDx64Target() && !global.params.isLP64) 286 { 287 // For AMD64 ILP32 ABI, delegates fit into a single integer register. 288 const offset = cast(uint)Type.tsize_t.size(Loc.initial); 289 Type t = argtypemerge(Type.tvoidptr, Type.tvoidptr, offset); 290 if (t) 291 { 292 return oneType(t); 293 } 294 } 295 return twoTypes(Type.tvoidptr, Type.tvoidptr); 296 } 297 298 override void visit(TypeSArray t) 299 { 300 const sz = t.size(Loc.initial); 301 if (sz > 16) 302 return memory(); 303 304 const dim = t.dim.toInteger(); 305 Type tn = t.next; 306 const tnsize = tn.size(); 307 const tnalignsize = tn.alignsize(); 308 309 /***** 310 * Get the nth element of this array. 311 * Params: 312 * n = element number, from 0..dim 313 * offset = set to offset of the element from the start of the array 314 * alignsize = set to the aligned size of the element 315 * Returns: 316 * type of the element 317 */ 318 extern (D) Type getNthElement(size_t n, out uint offset, out uint alignsize) 319 { 320 offset = cast(uint)(n * tnsize); 321 alignsize = tnalignsize; 322 return tn; 323 } 324 325 aggregate(sz, cast(size_t)dim, &getNthElement); 326 } 327 328 override void visit(TypeStruct t) 329 { 330 //printf("TypeStruct.toArgTypes() %s\n", t.toChars()); 331 332 if (!t.sym.isPOD()) 333 return memory(); 334 335 /***** 336 * Get the nth field of this struct. 337 * Params: 338 * n = field number, from 0..nfields 339 * offset = set to offset of the field from the start of the type 340 * alignsize = set to the aligned size of the field 341 * Returns: 342 * type of the field 343 */ 344 extern (D) Type getNthField(size_t n, out uint offset, out uint alignsize) 345 { 346 auto field = t.sym.fields[n]; 347 offset = field.offset; 348 alignsize = field.type.alignsize(); 349 return field.type; 350 } 351 352 aggregate(t.size(Loc.initial), t.sym.fields.dim, &getNthField); 353 } 354 355 /******************* 356 * Handle aggregates (struct, union, and static array) and set `result` 357 * Params: 358 * sz = total size of aggregate 359 * nfields = number of fields in the aggregate (dimension for static arrays) 360 * getFieldInfo = get information about the nth field in the aggregate 361 */ 362 extern (D) void aggregate(d_uns64 sz, size_t nfields, Type delegate(size_t, out uint, out uint) getFieldInfo) 363 { 364 if (nfields == 0) 365 return memory(); 366 367 if (isDMDx64Target()) 368 { 369 if (sz == 0 || sz > 16) 370 return memory(); 371 372 Type t1 = null; 373 Type t2 = null; 374 375 foreach (n; 0 .. nfields) 376 { 377 uint foffset; 378 uint falignsize; 379 Type ftype = getFieldInfo(n, foffset, falignsize); 380 381 //printf(" [%u] ftype = %s\n", n, ftype.toChars()); 382 TypeTuple tup = toArgTypes(ftype); 383 if (!tup) 384 return memory(); 385 const dim = tup.arguments.dim; 386 Type ft1 = null; 387 Type ft2 = null; 388 switch (dim) 389 { 390 case 2: 391 ft1 = (*tup.arguments)[0].type; 392 ft2 = (*tup.arguments)[1].type; 393 break; 394 case 1: 395 if (foffset < 8) 396 ft1 = (*tup.arguments)[0].type; 397 else 398 ft2 = (*tup.arguments)[0].type; 399 break; 400 default: 401 return memory(); 402 } 403 if (foffset & 7) 404 { 405 // Misaligned fields goto Lmemory 406 if (foffset & (falignsize - 1)) 407 return memory(); 408 409 // Fields that overlap the 8byte boundary goto memory 410 const fieldsz = ftype.size(Loc.initial); 411 bool overflow; 412 const nextOffset = addu(foffset, fieldsz, overflow); 413 assert(!overflow); 414 if (foffset < 8 && nextOffset > 8) 415 return memory(); 416 } 417 // First field in 8byte must be at start of 8byte 418 assert(t1 || foffset == 0); 419 //printf("ft1 = %s\n", ft1 ? ft1.toChars() : "null"); 420 //printf("ft2 = %s\n", ft2 ? ft2.toChars() : "null"); 421 if (ft1) 422 { 423 t1 = argtypemerge(t1, ft1, foffset); 424 if (!t1) 425 return memory(); 426 } 427 if (ft2) 428 { 429 const off2 = ft1 ? 8 : foffset; 430 if (!t2 && off2 != 8) 431 return memory(); 432 assert(t2 || off2 == 8); 433 t2 = argtypemerge(t2, ft2, off2 - 8); 434 if (!t2) 435 return memory(); 436 } 437 } 438 if (t2) 439 { 440 if (t1.isfloating() && t2.isfloating()) 441 { 442 if ((t1.ty == Tfloat32 || t1.ty == Tfloat64) && (t2.ty == Tfloat32 || t2.ty == Tfloat64)) 443 { 444 } 445 else 446 return memory(); 447 } 448 else if (t1.isfloating() || t2.isfloating()) 449 return memory(); 450 return twoTypes(t1, t2); 451 } 452 453 //printf("\ttoArgTypes() %s => [%s,%s]\n", t.toChars(), t1 ? t1.toChars() : "", t2 ? t2.toChars() : ""); 454 if (t1) 455 return oneType(t1); 456 else 457 return memory(); 458 } 459 else 460 { 461 Type t1 = null; 462 switch (cast(uint)sz) 463 { 464 case 1: 465 t1 = Type.tint8; 466 break; 467 case 2: 468 t1 = Type.tint16; 469 break; 470 case 4: 471 t1 = Type.tint32; 472 break; 473 case 8: 474 t1 = Type.tint64; 475 break; 476 case 16: 477 t1 = null; // could be a TypeVector 478 break; 479 default: 480 return memory(); 481 } 482 if (global.params.isFreeBSD && nfields == 1 && 483 (sz == 4 || sz == 8)) 484 { 485 /* FreeBSD changed their 32 bit ABI at some point before 10.3 for the following: 486 * struct { float f; } => arg1type is float 487 * struct { double d; } => arg1type is double 488 * Cannot find any documentation on it. 489 */ 490 491 uint foffset; 492 uint falignsize; 493 Type ftype = getFieldInfo(0, foffset, falignsize); 494 TypeTuple tup = toArgTypes(ftype); 495 if (tup && tup.arguments.dim == 1) 496 { 497 Type ft1 = (*tup.arguments)[0].type; 498 if (ft1.ty == Tfloat32 || ft1.ty == Tfloat64) 499 return oneType(ft1); 500 } 501 } 502 503 if (t1) 504 return oneType(t1); 505 else 506 return memory(); 507 } 508 } 509 510 override void visit(TypeEnum t) 511 { 512 t.toBasetype().accept(this); 513 } 514 515 override void visit(TypeClass) 516 { 517 result = new TypeTuple(Type.tvoidptr); 518 } 519 } 520 521 scope ToArgTypes v = new ToArgTypes(); 522 t.accept(v); 523 return v.result; 524 }