1 /** 2 * Handles target-specific parameters 3 * 4 * In order to allow for cross compilation, when the compiler produces a binary 5 * for a different platform than it is running on, target information needs 6 * to be abstracted. This is done in this module, primarily through `Target`. 7 * 8 * Note: 9 * While DMD itself does not support cross-compilation, GDC and LDC do. 10 * Hence, this module is (sometimes heavily) modified by them, 11 * and contributors should review how their changes affect them. 12 * 13 * See_Also: 14 * - $(LINK2 https://wiki.osdev.org/Target_Triplet, Target Triplets) 15 * - $(LINK2 https://github.com/ldc-developers/ldc, LDC repository) 16 * - $(LINK2 https://github.com/D-Programming-GDC/gcc, GDC repository) 17 * 18 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 19 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 20 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 21 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/target.d, _target.d) 22 * Documentation: https://dlang.org/phobos/dmd_target.html 23 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/target.d 24 */ 25 26 module dmd.target; 27 28 import dmd.argtypes_x86; 29 import dmd.argtypes_sysv_x64; 30 import core.stdc.string : strlen; 31 import dmd.cppmangle; 32 import dmd.cppmanglewin; 33 import dmd.dclass; 34 import dmd.declaration; 35 import dmd.dscope; 36 import dmd.dstruct; 37 import dmd.dsymbol; 38 import dmd.expression; 39 import dmd.func; 40 import dmd.globals; 41 import dmd.id; 42 import dmd.identifier; 43 import dmd.mtype; 44 import dmd.root.rmem; 45 import dmd.typesem; 46 import dmd.tokens : TOK; 47 import dmd.root.ctfloat; 48 import dmd.root.outbuffer; 49 import dmd.root.string : toDString; 50 51 //////////////////////////////////////////////////////////////////////////////// 52 /** 53 * Describes a back-end target. At present it is incomplete, but in the future 54 * it should grow to contain most or all target machine and target O/S specific 55 * information. 56 * 57 * In many cases, calls to sizeof() can't be used directly for getting data type 58 * sizes since cross compiling is supported and would end up using the host 59 * sizes rather than the target sizes. 60 */ 61 extern (C++) struct Target 62 { 63 // D ABI 64 uint ptrsize; /// size of a pointer in bytes 65 uint realsize; /// size a real consumes in memory 66 uint realpad; /// padding added to the CPU real size to bring it up to realsize 67 uint realalignsize; /// alignment for reals 68 uint classinfosize; /// size of `ClassInfo` 69 ulong maxStaticDataSize; /// maximum size of static data 70 71 /// C ABI 72 TargetC c; 73 74 /// C++ ABI 75 TargetCPP cpp; 76 77 /// Objective-C ABI 78 TargetObjC objc; 79 80 /// Architecture name 81 const(char)[] architectureName; 82 83 /** 84 * Values representing all properties for floating point types 85 */ 86 extern (C++) struct FPTypeProperties(T) 87 { 88 real_t max; /// largest representable value that's not infinity 89 real_t min_normal; /// smallest representable normalized value that's not 0 90 real_t nan; /// NaN value 91 real_t infinity; /// infinity value 92 real_t epsilon; /// smallest increment to the value 1 93 94 d_int64 dig = T.dig; /// number of decimal digits of precision 95 d_int64 mant_dig = T.mant_dig; /// number of bits in mantissa 96 d_int64 max_exp = T.max_exp; /// maximum int value such that 2$(SUPERSCRIPT `max_exp-1`) is representable 97 d_int64 min_exp = T.min_exp; /// minimum int value such that 2$(SUPERSCRIPT `min_exp-1`) is representable as a normalized value 98 d_int64 max_10_exp = T.max_10_exp; /// maximum int value such that 10$(SUPERSCRIPT `max_10_exp` is representable) 99 d_int64 min_10_exp = T.min_10_exp; /// minimum int value such that 10$(SUPERSCRIPT `min_10_exp`) is representable as a normalized value 100 101 extern (D) void initialize() 102 { 103 max = T.max; 104 min_normal = T.min_normal; 105 nan = T.nan; 106 infinity = T.infinity; 107 epsilon = T.epsilon; 108 } 109 } 110 111 FPTypeProperties!float FloatProperties; /// 112 FPTypeProperties!double DoubleProperties; /// 113 FPTypeProperties!real_t RealProperties; /// 114 115 private Type tvalist; // cached lazy result of va_listType() 116 117 private const(Param)* params; // cached reference to global.params 118 119 /** 120 * Initialize the Target 121 */ 122 extern (C++) void _init(ref const Param params) 123 { 124 this.params = ¶ms; 125 126 FloatProperties.initialize(); 127 DoubleProperties.initialize(); 128 RealProperties.initialize(); 129 130 // These have default values for 32 bit code, they get 131 // adjusted for 64 bit code. 132 ptrsize = 4; 133 classinfosize = 0x4C; // 76 134 135 /* gcc uses int.max for 32 bit compilations, and long.max for 64 bit ones. 136 * Set to int.max for both, because the rest of the compiler cannot handle 137 * 2^64-1 without some pervasive rework. The trouble is that much of the 138 * front and back end uses 32 bit ints for sizes and offsets. Since C++ 139 * silently truncates 64 bit ints to 32, finding all these dependencies will be a problem. 140 */ 141 maxStaticDataSize = int.max; 142 143 if (params.isLP64) 144 { 145 ptrsize = 8; 146 classinfosize = 0x98; // 152 147 } 148 if (params.isLinux || params.isFreeBSD || params.isOpenBSD || params.isDragonFlyBSD || params.isSolaris) 149 { 150 realsize = 12; 151 realpad = 2; 152 realalignsize = 4; 153 } 154 else if (params.isOSX) 155 { 156 realsize = 16; 157 realpad = 6; 158 realalignsize = 16; 159 } 160 else if (params.isWindows) 161 { 162 realsize = 10; 163 realpad = 0; 164 realalignsize = 2; 165 if (ptrsize == 4) 166 { 167 /* Optlink cannot deal with individual data chunks 168 * larger than 16Mb 169 */ 170 maxStaticDataSize = 0x100_0000; // 16Mb 171 } 172 } 173 else 174 assert(0); 175 if (params.is64bit) 176 { 177 if (params.isLinux || params.isFreeBSD || params.isDragonFlyBSD || params.isSolaris) 178 { 179 realsize = 16; 180 realpad = 6; 181 realalignsize = 16; 182 } 183 } 184 185 c.initialize(params, this); 186 cpp.initialize(params, this); 187 objc.initialize(params, this); 188 189 if (global.params.is64bit) 190 architectureName = "X86_64"; 191 else 192 architectureName = "X86"; 193 } 194 195 /** 196 * Deinitializes the global state of the compiler. 197 * 198 * This can be used to restore the state set by `_init` to its original 199 * state. 200 */ 201 void deinitialize() 202 { 203 this = this.init; 204 } 205 206 /** 207 * Requested target memory alignment size of the given type. 208 * Params: 209 * type = type to inspect 210 * Returns: 211 * alignment in bytes 212 */ 213 extern (C++) uint alignsize(Type type) 214 { 215 assert(type.isTypeBasic()); 216 switch (type.ty) 217 { 218 case Tfloat80: 219 case Timaginary80: 220 case Tcomplex80: 221 return target.realalignsize; 222 case Tcomplex32: 223 if (params.isLinux || params.isOSX || params.isFreeBSD || params.isOpenBSD || 224 params.isDragonFlyBSD || params.isSolaris) 225 return 4; 226 break; 227 case Tint64: 228 case Tuns64: 229 case Tfloat64: 230 case Timaginary64: 231 case Tcomplex64: 232 if (params.isLinux || params.isOSX || params.isFreeBSD || params.isOpenBSD || 233 params.isDragonFlyBSD || params.isSolaris) 234 return params.is64bit ? 8 : 4; 235 break; 236 default: 237 break; 238 } 239 return cast(uint)type.size(Loc.initial); 240 } 241 242 /** 243 * Requested target field alignment size of the given type. 244 * Params: 245 * type = type to inspect 246 * Returns: 247 * alignment in bytes 248 */ 249 extern (C++) uint fieldalign(Type type) 250 { 251 const size = type.alignsize(); 252 253 if ((params.is64bit || params.isOSX) && (size == 16 || size == 32)) 254 return size; 255 256 return (8 < size) ? 8 : size; 257 } 258 259 /** 260 * Size of the target OS critical section. 261 * Returns: 262 * size in bytes 263 */ 264 extern (C++) uint critsecsize() 265 { 266 return c.criticalSectionSize; 267 } 268 269 /** 270 * Type for the `va_list` type for the target; e.g., required for `_argptr` 271 * declarations. 272 * NOTE: For Posix/x86_64 this returns the type which will really 273 * be used for passing an argument of type va_list. 274 * Returns: 275 * `Type` that represents `va_list`. 276 */ 277 extern (C++) Type va_listType(const ref Loc loc, Scope* sc) 278 { 279 if (tvalist) 280 return tvalist; 281 282 if (params.isWindows) 283 { 284 tvalist = Type.tchar.pointerTo(); 285 } 286 else if (params.isLinux || params.isFreeBSD || params.isOpenBSD || 287 params.isDragonFlyBSD || params.isSolaris || params.isOSX) 288 { 289 if (params.is64bit) 290 { 291 tvalist = Pool!TypeIdentifier.make(Loc.initial, Identifier.idPool("__va_list_tag")).pointerTo(); 292 tvalist = typeSemantic(tvalist, loc, sc); 293 } 294 else 295 { 296 tvalist = Type.tchar.pointerTo(); 297 } 298 } 299 else 300 { 301 assert(0); 302 } 303 304 return tvalist; 305 } 306 307 /** 308 * Checks whether the target supports a vector type. 309 * Params: 310 * sz = vector type size in bytes 311 * type = vector element type 312 * Returns: 313 * 0 vector type is supported, 314 * 1 vector type is not supported on the target at all 315 * 2 vector element type is not supported 316 * 3 vector size is not supported 317 */ 318 extern (C++) int isVectorTypeSupported(int sz, Type type) 319 { 320 if (!isXmmSupported()) 321 return 1; // not supported 322 323 switch (type.ty) 324 { 325 case Tvoid: 326 case Tint8: 327 case Tuns8: 328 case Tint16: 329 case Tuns16: 330 case Tint32: 331 case Tuns32: 332 case Tfloat32: 333 case Tint64: 334 case Tuns64: 335 case Tfloat64: 336 break; 337 default: 338 return 2; // wrong base type 339 } 340 341 // Whether a vector is really supported depends on the CPU being targeted. 342 if (sz == 16) 343 { 344 final switch (type.ty) 345 { 346 case Tint32: 347 case Tuns32: 348 case Tfloat32: 349 if (params.cpu < CPU.sse) 350 return 3; // no SSE vector support 351 break; 352 353 case Tvoid: 354 case Tint8: 355 case Tuns8: 356 case Tint16: 357 case Tuns16: 358 case Tint64: 359 case Tuns64: 360 case Tfloat64: 361 if (params.cpu < CPU.sse2) 362 return 3; // no SSE2 vector support 363 break; 364 } 365 } 366 else if (sz == 32) 367 { 368 if (params.cpu < CPU.avx) 369 return 3; // no AVX vector support 370 } 371 else 372 return 3; // wrong size 373 374 return 0; 375 } 376 377 /** 378 * Checks whether the target supports the given operation for vectors. 379 * Params: 380 * type = target type of operation 381 * op = the unary or binary op being done on the `type` 382 * t2 = type of second operand if `op` is a binary operation 383 * Returns: 384 * true if the operation is supported or type is not a vector 385 */ 386 extern (C++) bool isVectorOpSupported(Type type, ubyte op, Type t2 = null) 387 { 388 import dmd.tokens; 389 390 if (type.ty != Tvector) 391 return true; // not a vector op 392 auto tvec = cast(TypeVector) type; 393 const vecsize = cast(int)tvec.basetype.size(); 394 const elemty = cast(int)tvec.elementType().ty; 395 396 // Only operations on these sizes are supported (see isVectorTypeSupported) 397 if (vecsize != 16 && vecsize != 32) 398 return false; 399 400 bool supported = false; 401 switch (op) 402 { 403 case TOK.uadd: 404 // Expression is a no-op, supported everywhere. 405 supported = tvec.isscalar(); 406 break; 407 408 case TOK.negate: 409 if (vecsize == 16) 410 { 411 // float[4] negate needs SSE support ({V}SUBPS) 412 if (elemty == Tfloat32 && params.cpu >= CPU.sse) 413 supported = true; 414 // double[2] negate needs SSE2 support ({V}SUBPD) 415 else if (elemty == Tfloat64 && params.cpu >= CPU.sse2) 416 supported = true; 417 // (u)byte[16]/short[8]/int[4]/long[2] negate needs SSE2 support ({V}PSUB[BWDQ]) 418 else if (tvec.isintegral() && params.cpu >= CPU.sse2) 419 supported = true; 420 } 421 else if (vecsize == 32) 422 { 423 // float[8]/double[4] negate needs AVX support (VSUBP[SD]) 424 if (tvec.isfloating() && params.cpu >= CPU.avx) 425 supported = true; 426 // (u)byte[32]/short[16]/int[8]/long[4] negate needs AVX2 support (VPSUB[BWDQ]) 427 else if (tvec.isintegral() && params.cpu >= CPU.avx2) 428 supported = true; 429 } 430 break; 431 432 case TOK.lessThan, TOK.greaterThan, TOK.lessOrEqual, TOK.greaterOrEqual, TOK.equal, TOK.notEqual, TOK.identity, TOK.notIdentity: 433 supported = false; 434 break; 435 436 case TOK.leftShift, TOK.leftShiftAssign, TOK.rightShift, TOK.rightShiftAssign, TOK.unsignedRightShift, TOK.unsignedRightShiftAssign: 437 supported = false; 438 break; 439 440 case TOK.add, TOK.addAssign, TOK.min, TOK.minAssign: 441 if (vecsize == 16) 442 { 443 // float[4] add/sub needs SSE support ({V}ADDPS, {V}SUBPS) 444 if (elemty == Tfloat32 && params.cpu >= CPU.sse) 445 supported = true; 446 // double[2] add/sub needs SSE2 support ({V}ADDPD, {V}SUBPD) 447 else if (elemty == Tfloat64 && params.cpu >= CPU.sse2) 448 supported = true; 449 // (u)byte[16]/short[8]/int[4]/long[2] add/sub needs SSE2 support ({V}PADD[BWDQ], {V}PSUB[BWDQ]) 450 else if (tvec.isintegral() && params.cpu >= CPU.sse2) 451 supported = true; 452 } 453 else if (vecsize == 32) 454 { 455 // float[8]/double[4] add/sub needs AVX support (VADDP[SD], VSUBP[SD]) 456 if (tvec.isfloating() && params.cpu >= CPU.avx) 457 supported = true; 458 // (u)byte[32]/short[16]/int[8]/long[4] add/sub needs AVX2 support (VPADD[BWDQ], VPSUB[BWDQ]) 459 else if (tvec.isintegral() && params.cpu >= CPU.avx2) 460 supported = true; 461 } 462 break; 463 464 case TOK.mul, TOK.mulAssign: 465 if (vecsize == 16) 466 { 467 // float[4] multiply needs SSE support ({V}MULPS) 468 if (elemty == Tfloat32 && params.cpu >= CPU.sse) 469 supported = true; 470 // double[2] multiply needs SSE2 support ({V}MULPD) 471 else if (elemty == Tfloat64 && params.cpu >= CPU.sse2) 472 supported = true; 473 // (u)short[8] multiply needs SSE2 support ({V}PMULLW) 474 else if ((elemty == Tint16 || elemty == Tuns16) && params.cpu >= CPU.sse2) 475 supported = true; 476 // (u)int[4] multiply needs SSE4.1 support ({V}PMULLD) 477 else if ((elemty == Tint32 || elemty == Tuns32) && params.cpu >= CPU.sse4_1) 478 supported = true; 479 } 480 else if (vecsize == 32) 481 { 482 // float[8]/double[4] multiply needs AVX support (VMULP[SD]) 483 if (tvec.isfloating() && params.cpu >= CPU.avx) 484 supported = true; 485 // (u)short[16] multiply needs AVX2 support (VPMULLW) 486 else if ((elemty == Tint16 || elemty == Tuns16) && params.cpu >= CPU.avx2) 487 supported = true; 488 // (u)int[8] multiply needs AVX2 support (VPMULLD) 489 else if ((elemty == Tint32 || elemty == Tuns32) && params.cpu >= CPU.avx2) 490 supported = true; 491 } 492 break; 493 494 case TOK.div, TOK.divAssign: 495 if (vecsize == 16) 496 { 497 // float[4] divide needs SSE support ({V}DIVPS) 498 if (elemty == Tfloat32 && params.cpu >= CPU.sse) 499 supported = true; 500 // double[2] divide needs SSE2 support ({V}DIVPD) 501 else if (elemty == Tfloat64 && params.cpu >= CPU.sse2) 502 supported = true; 503 } 504 else if (vecsize == 32) 505 { 506 // float[8]/double[4] multiply needs AVX support (VDIVP[SD]) 507 if (tvec.isfloating() && params.cpu >= CPU.avx) 508 supported = true; 509 } 510 break; 511 512 case TOK.mod, TOK.modAssign: 513 supported = false; 514 break; 515 516 case TOK.and, TOK.andAssign, TOK.or, TOK.orAssign, TOK.xor, TOK.xorAssign: 517 // (u)byte[16]/short[8]/int[4]/long[2] bitwise ops needs SSE2 support ({V}PAND, {V}POR, {V}PXOR) 518 if (vecsize == 16 && tvec.isintegral() && params.cpu >= CPU.sse2) 519 supported = true; 520 // (u)byte[32]/short[16]/int[8]/long[4] bitwise ops needs AVX2 support (VPAND, VPOR, VPXOR) 521 else if (vecsize == 32 && tvec.isintegral() && params.cpu >= CPU.avx2) 522 supported = true; 523 break; 524 525 case TOK.not: 526 supported = false; 527 break; 528 529 case TOK.tilde: 530 // (u)byte[16]/short[8]/int[4]/long[2] logical exclusive needs SSE2 support ({V}PXOR) 531 if (vecsize == 16 && tvec.isintegral() && params.cpu >= CPU.sse2) 532 supported = true; 533 // (u)byte[32]/short[16]/int[8]/long[4] logical exclusive needs AVX2 support (VPXOR) 534 else if (vecsize == 32 && tvec.isintegral() && params.cpu >= CPU.avx2) 535 supported = true; 536 break; 537 538 case TOK.pow, TOK.powAssign: 539 supported = false; 540 break; 541 542 default: 543 // import std.stdio : stderr, writeln; 544 // stderr.writeln(op); 545 assert(0, "unhandled op " ~ Token.toString(cast(TOK)op)); 546 } 547 return supported; 548 } 549 550 /** 551 * Default system linkage for the target. 552 * Returns: 553 * `LINK` to use for `extern(System)` 554 */ 555 extern (C++) LINK systemLinkage() 556 { 557 return params.isWindows ? LINK.windows : LINK.c; 558 } 559 560 /** 561 * Describes how an argument type is passed to a function on target. 562 * Params: 563 * t = type to break down 564 * Returns: 565 * tuple of types if type is passed in one or more registers 566 * empty tuple if type is always passed on the stack 567 * null if the type is a `void` or argtypes aren't supported by the target 568 */ 569 extern (C++) TypeTuple toArgTypes(Type t) 570 { 571 if (params.is64bit) 572 { 573 // no argTypes for Win64 yet 574 return isPOSIX ? toArgTypes_sysv_x64(t) : null; 575 } 576 return toArgTypes_x86(t); 577 } 578 579 /** 580 * Determine return style of function - whether in registers or 581 * through a hidden pointer to the caller's stack. 582 * Params: 583 * tf = function type to check 584 * needsThis = true if the function type is for a non-static member function 585 * Returns: 586 * true if return value from function is on the stack 587 */ 588 extern (C++) bool isReturnOnStack(TypeFunction tf, bool needsThis) 589 { 590 if (tf.isref) 591 { 592 //printf(" ref false\n"); 593 return false; // returns a pointer 594 } 595 596 Type tn = tf.next.toBasetype(); 597 //printf("tn = %s\n", tn.toChars()); 598 d_uns64 sz = tn.size(); 599 Type tns = tn; 600 601 if (params.isWindows && params.is64bit) 602 { 603 // http://msdn.microsoft.com/en-us/library/7572ztz4.aspx 604 if (tns.ty == Tcomplex32) 605 return true; 606 if (tns.isscalar()) 607 return false; 608 609 tns = tns.baseElemOf(); 610 if (tns.ty == Tstruct) 611 { 612 StructDeclaration sd = (cast(TypeStruct)tns).sym; 613 if (tf.linkage == LINK.cpp && needsThis) 614 return true; 615 if (!sd.isPOD() || sz > 8) 616 return true; 617 if (sd.fields.dim == 0) 618 return true; 619 } 620 if (sz <= 16 && !(sz & (sz - 1))) 621 return false; 622 return true; 623 } 624 else if (params.isWindows && params.mscoff) 625 { 626 Type tb = tns.baseElemOf(); 627 if (tb.ty == Tstruct) 628 { 629 if (tf.linkage == LINK.cpp && needsThis) 630 return true; 631 } 632 } 633 else if (params.is64bit && isPOSIX) 634 { 635 TypeTuple tt = .toArgTypes_sysv_x64(tn); 636 if (!tt) 637 return false; // void 638 else 639 return !tt.arguments.dim; 640 } 641 642 Lagain: 643 if (tns.ty == Tsarray) 644 { 645 tns = tns.baseElemOf(); 646 if (tns.ty != Tstruct) 647 { 648 L2: 649 if (params.isLinux && tf.linkage != LINK.d && !params.is64bit) 650 { 651 // 32 bit C/C++ structs always on stack 652 } 653 else 654 { 655 switch (sz) 656 { 657 case 1: 658 case 2: 659 case 4: 660 case 8: 661 //printf(" sarray false\n"); 662 return false; // return small structs in regs 663 // (not 3 byte structs!) 664 default: 665 break; 666 } 667 } 668 //printf(" sarray true\n"); 669 return true; 670 } 671 } 672 673 if (tns.ty == Tstruct) 674 { 675 StructDeclaration sd = (cast(TypeStruct)tns).sym; 676 if (params.isLinux && tf.linkage != LINK.d && !params.is64bit) 677 { 678 //printf(" 2 true\n"); 679 return true; // 32 bit C/C++ structs always on stack 680 } 681 if (params.isWindows && tf.linkage == LINK.cpp && !params.is64bit && 682 sd.isPOD() && sd.ctor) 683 { 684 // win32 returns otherwise POD structs with ctors via memory 685 return true; 686 } 687 if (sd.numArgTypes() == 1) 688 { 689 tns = sd.argType(0); 690 if (tns.ty != Tstruct) 691 goto L2; 692 goto Lagain; 693 } 694 else if (params.is64bit && sd.numArgTypes() == 0) 695 return true; 696 else if (sd.isPOD()) 697 { 698 switch (sz) 699 { 700 case 1: 701 case 2: 702 case 4: 703 case 8: 704 //printf(" 3 false\n"); 705 return false; // return small structs in regs 706 // (not 3 byte structs!) 707 case 16: 708 if (!params.isWindows && params.is64bit) 709 return false; 710 break; 711 712 default: 713 break; 714 } 715 } 716 //printf(" 3 true\n"); 717 return true; 718 } 719 else if ((params.isLinux || params.isOSX || 720 params.isFreeBSD || params.isSolaris || 721 params.isDragonFlyBSD) && 722 tf.linkage == LINK.c && 723 tns.iscomplex()) 724 { 725 if (tns.ty == Tcomplex32) 726 return false; // in EDX:EAX, not ST1:ST0 727 else 728 return true; 729 } 730 else if (params.isWindows && 731 !params.is64bit && 732 (tf.linkage == LINK.cpp || tf.linkage == LINK.pascal) && 733 tf.isfloating()) 734 { 735 /* See DMC++ function exp2_retmethod() 736 * https://github.com/DigitalMars/Compiler/blob/master/dm/src/dmc/dexp2.d#L149 737 */ 738 return true; 739 } 740 else 741 { 742 //assert(sz <= 16); 743 //printf(" 4 false\n"); 744 return false; 745 } 746 } 747 748 /*** 749 * Determine the size a value of type `t` will be when it 750 * is passed on the function parameter stack. 751 * Params: 752 * loc = location to use for error messages 753 * t = type of parameter 754 * Returns: 755 * size used on parameter stack 756 */ 757 extern (C++) ulong parameterSize(const ref Loc loc, Type t) 758 { 759 if (!params.is64bit && 760 (params.isFreeBSD || params.isOSX)) 761 { 762 /* These platforms use clang, which regards a struct 763 * with size 0 as being of size 0 on the parameter stack, 764 * even while sizeof(struct) is 1. 765 * It's an ABI incompatibility with gcc. 766 */ 767 if (t.ty == Tstruct) 768 { 769 auto ts = cast(TypeStruct)t; 770 if (ts.sym.hasNoFields) 771 return 0; 772 } 773 } 774 const sz = t.size(loc); 775 return params.is64bit ? (sz + 7) & ~7 : (sz + 3) & ~3; 776 } 777 778 /** 779 * Determine which `in` parameters needs to be passed by `ref` 780 * 781 * Called from `TypeFunction` semantic with the full function type. 782 * This routine must iterate over parameters, and may set `STC.ref_` 783 * for any parameter which already have `STC.in_`. 784 * This hook must never set `STC.ref_` if the parameter is not `STC.in_`, 785 * nor should it ever change anything else. 786 * 787 * This hook will not be called when `-preview=in` wasn't passed to the 788 * frontend, hence it needs not care about `params.previewIn`. 789 * 790 * Params: 791 * tf = Type of the function to inspect. The type will have its 792 * parameter types semantically resolved, however other attributes 793 * (return type, `@safe` / `nothrow`, etc...) must not be used. 794 */ 795 extern(C++) void applyInRefParams (TypeFunction tf) 796 { 797 foreach (_idx, p; tf.parameterList) 798 { 799 // Ignore non-`in` or already-`ref` parameters 800 if ((p.storageClass & (STC.in_ | STC.ref_)) != STC.in_) 801 continue; 802 803 assert(p.type !is null); 804 // If it has a copy constructor / destructor / postblit, 805 // it is always by ref 806 if (p.type.needsDestruction() || p.type.needsCopyOrPostblit()) 807 p.storageClass |= STC.ref_; 808 // If the type can't be copied, always by `ref` 809 else if (!p.type.isCopyable()) 810 p.storageClass |= STC.ref_; 811 // The Win64 ABI requires x87 real to be passed by ref 812 else if (params.isWindows && params.is64bit && 813 p.type.ty == Tfloat80) 814 p.storageClass |= STC.ref_; 815 816 // If it's a dynamic array, use the value type as it 817 // allows covariance between `in char[]` and `scope const(char)[]` 818 // The same reasoning applies to pointers and classes, 819 // but that is handled by the `(sz > 8)` below. 820 else if (p.type.ty == Tarray) 821 continue; 822 // Pass delegates by value to allow covariance 823 // Function pointers are a single pointers and handled below. 824 else if (p.type.ty == Tdelegate) 825 continue; 826 else 827 { 828 const sz = p.type.size(); 829 if (params.is64bit ? (sz > 16) : (sz > 8)) 830 p.storageClass |= STC.ref_; 831 } 832 } 833 } 834 835 // this guarantees `getTargetInfo` and `allTargetInfos` remain in sync 836 private enum TargetInfoKeys 837 { 838 cppRuntimeLibrary, 839 cppStd, 840 floatAbi, 841 objectFormat, 842 } 843 844 /** 845 * Get targetInfo by key 846 * Params: 847 * name = name of targetInfo to get 848 * loc = location to use for error messages 849 * Returns: 850 * Expression for the requested targetInfo 851 */ 852 extern (C++) Expression getTargetInfo(const(char)* name, const ref Loc loc) 853 { 854 StringExp stringExp(const(char)[] sval) 855 { 856 return new StringExp(loc, sval); 857 } 858 859 switch (name.toDString) with (TargetInfoKeys) 860 { 861 case objectFormat.stringof: 862 if (params.isWindows) 863 return stringExp(params.mscoff ? "coff" : "omf"); 864 else if (params.isOSX) 865 return stringExp("macho"); 866 else 867 return stringExp("elf"); 868 case floatAbi.stringof: 869 return stringExp("hard"); 870 case cppRuntimeLibrary.stringof: 871 if (params.isWindows) 872 { 873 if (params.mscoff) 874 return stringExp(params.mscrtlib); 875 return stringExp("snn"); 876 } 877 return stringExp(""); 878 case cppStd.stringof: 879 return new IntegerExp(params.cplusplus); 880 881 default: 882 return null; 883 } 884 } 885 886 //////////////////////////////////////////////////////////////////////////// 887 /* All functions after this point are extern (D), as they are only relevant 888 * for targets of DMD, and should not be used in front-end code. 889 */ 890 891 /****************** 892 * Returns: 893 * true if xmm usage is supported 894 */ 895 extern (D) bool isXmmSupported() 896 { 897 return global.params.is64bit || global.params.isOSX; 898 } 899 900 /** 901 * Returns: 902 * true if generating code for POSIX 903 */ 904 extern (D) @property bool isPOSIX() scope const nothrow @nogc 905 out(result) { assert(result || params.isWindows); } 906 do 907 { 908 return params.isLinux 909 || params.isOSX 910 || params.isFreeBSD 911 || params.isOpenBSD 912 || params.isDragonFlyBSD 913 || params.isSolaris; 914 } 915 } 916 917 //////////////////////////////////////////////////////////////////////////////// 918 /** 919 * Functions and variables specific to interfacing with extern(C) ABI. 920 */ 921 struct TargetC 922 { 923 uint longsize; /// size of a C `long` or `unsigned long` type 924 uint long_doublesize; /// size of a C `long double` 925 uint criticalSectionSize; /// size of os critical section 926 927 extern (D) void initialize(ref const Param params, ref const Target target) 928 { 929 if (params.isLinux || params.isFreeBSD || params.isOpenBSD || params.isDragonFlyBSD || params.isSolaris) 930 longsize = 4; 931 else if (params.isOSX) 932 longsize = 4; 933 else if (params.isWindows) 934 longsize = 4; 935 else 936 assert(0); 937 if (params.is64bit) 938 { 939 if (params.isLinux || params.isFreeBSD || params.isDragonFlyBSD || params.isSolaris) 940 longsize = 8; 941 else if (params.isOSX) 942 longsize = 8; 943 } 944 if (params.is64bit && params.isWindows) 945 long_doublesize = 8; 946 else 947 long_doublesize = target.realsize; 948 949 criticalSectionSize = getCriticalSectionSize(params); 950 } 951 952 private static uint getCriticalSectionSize(ref const Param params) pure 953 { 954 if (params.isWindows) 955 { 956 // sizeof(CRITICAL_SECTION) for Windows. 957 return params.isLP64 ? 40 : 24; 958 } 959 else if (params.isLinux) 960 { 961 // sizeof(pthread_mutex_t) for Linux. 962 if (params.is64bit) 963 return params.isLP64 ? 40 : 32; 964 else 965 return params.isLP64 ? 40 : 24; 966 } 967 else if (params.isFreeBSD) 968 { 969 // sizeof(pthread_mutex_t) for FreeBSD. 970 return params.isLP64 ? 8 : 4; 971 } 972 else if (params.isOpenBSD) 973 { 974 // sizeof(pthread_mutex_t) for OpenBSD. 975 return params.isLP64 ? 8 : 4; 976 } 977 else if (params.isDragonFlyBSD) 978 { 979 // sizeof(pthread_mutex_t) for DragonFlyBSD. 980 return params.isLP64 ? 8 : 4; 981 } 982 else if (params.isOSX) 983 { 984 // sizeof(pthread_mutex_t) for OSX. 985 return params.isLP64 ? 64 : 44; 986 } 987 else if (params.isSolaris) 988 { 989 // sizeof(pthread_mutex_t) for Solaris. 990 return 24; 991 } 992 assert(0); 993 } 994 } 995 996 //////////////////////////////////////////////////////////////////////////////// 997 /** 998 * Functions and variables specific to interface with extern(C++) ABI. 999 */ 1000 struct TargetCPP 1001 { 1002 bool reverseOverloads; /// set if overloaded functions are grouped and in reverse order (such as in dmc and cl) 1003 bool exceptions; /// set if catching C++ exceptions is supported 1004 bool twoDtorInVtable; /// target C++ ABI puts deleting and non-deleting destructor into vtable 1005 1006 extern (D) void initialize(ref const Param params, ref const Target target) 1007 { 1008 if (params.isLinux || params.isFreeBSD || params.isOpenBSD || params.isDragonFlyBSD || params.isSolaris) 1009 twoDtorInVtable = true; 1010 else if (params.isOSX) 1011 twoDtorInVtable = true; 1012 else if (params.isWindows) 1013 reverseOverloads = true; 1014 else 1015 assert(0); 1016 exceptions = params.isLinux || params.isFreeBSD || 1017 params.isDragonFlyBSD || params.isOSX; 1018 } 1019 1020 /** 1021 * Mangle the given symbol for C++ ABI. 1022 * Params: 1023 * s = declaration with C++ linkage 1024 * Returns: 1025 * string mangling of symbol 1026 */ 1027 extern (C++) const(char)* toMangle(Dsymbol s) 1028 { 1029 static if (TARGET.Linux || TARGET.OSX || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.DragonFlyBSD || TARGET.Solaris) 1030 return toCppMangleItanium(s); 1031 else static if (TARGET.Windows) 1032 return toCppMangleMSVC(s); 1033 else 1034 static assert(0, "fix this"); 1035 } 1036 1037 /** 1038 * Get RTTI mangling of the given class declaration for C++ ABI. 1039 * Params: 1040 * cd = class with C++ linkage 1041 * Returns: 1042 * string mangling of C++ typeinfo 1043 */ 1044 extern (C++) const(char)* typeInfoMangle(ClassDeclaration cd) 1045 { 1046 static if (TARGET.Linux || TARGET.OSX || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.Solaris || TARGET.DragonFlyBSD) 1047 return cppTypeInfoMangleItanium(cd); 1048 else static if (TARGET.Windows) 1049 return cppTypeInfoMangleMSVC(cd); 1050 else 1051 static assert(0, "fix this"); 1052 } 1053 1054 /** 1055 * Gets vendor-specific type mangling for C++ ABI. 1056 * Params: 1057 * t = type to inspect 1058 * Returns: 1059 * string if type is mangled specially on target 1060 * null if unhandled 1061 */ 1062 extern (C++) const(char)* typeMangle(Type t) 1063 { 1064 return null; 1065 } 1066 1067 /** 1068 * Get the type that will really be used for passing the given argument 1069 * to an `extern(C++)` function. 1070 * Params: 1071 * p = parameter to be passed. 1072 * Returns: 1073 * `Type` to use for parameter `p`. 1074 */ 1075 extern (C++) Type parameterType(Parameter p) 1076 { 1077 Type t = p.type.merge2(); 1078 if (p.isReference()) 1079 t = t.referenceTo(); 1080 else if (p.storageClass & STC.lazy_) 1081 { 1082 // Mangle as delegate 1083 Type td = new TypeFunction(ParameterList(), t, LINK.d); 1084 td = new TypeDelegate(td); 1085 t = merge(t); 1086 } 1087 return t; 1088 } 1089 1090 /** 1091 * Checks whether type is a vendor-specific fundamental type. 1092 * Params: 1093 * t = type to inspect 1094 * isFundamental = where to store result 1095 * Returns: 1096 * true if isFundamental was set by function 1097 */ 1098 extern (C++) bool fundamentalType(const Type t, ref bool isFundamental) 1099 { 1100 return false; 1101 } 1102 } 1103 1104 //////////////////////////////////////////////////////////////////////////////// 1105 /** 1106 * Functions and variables specific to interface with extern(Objective-C) ABI. 1107 */ 1108 struct TargetObjC 1109 { 1110 bool supported; /// set if compiler can interface with Objective-C 1111 1112 extern (D) void initialize(ref const Param params, ref const Target target) 1113 { 1114 if (params.isOSX && params.is64bit) 1115 supported = true; 1116 } 1117 } 1118 1119 //////////////////////////////////////////////////////////////////////////////// 1120 extern (C++) __gshared Target target;