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