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