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-2023 by The D Language Foundation, All Rights Reserved 19 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 20 * License: $(LINK2 https://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.globals : Param, CHECKENABLE; 29 30 enum CPU : ubyte 31 { 32 x87, 33 mmx, 34 sse, 35 sse2, 36 sse3, 37 ssse3, 38 sse4_1, 39 sse4_2, 40 avx, // AVX1 instruction set 41 avx2, // AVX2 instruction set 42 avx512, // AVX-512 instruction set 43 44 // Special values that don't survive past the command line processing 45 baseline, // (default) the minimum capability CPU 46 native // the machine the compiler is being run on 47 } 48 49 Target.OS defaultTargetOS() 50 { 51 version (Windows) 52 return Target.OS.Windows; 53 else version (linux) 54 return Target.OS.linux; 55 else version (OSX) 56 return Target.OS.OSX; 57 else version (FreeBSD) 58 return Target.OS.FreeBSD; 59 else version (OpenBSD) 60 return Target.OS.OpenBSD; 61 else version (Solaris) 62 return Target.OS.Solaris; 63 else version (DragonFlyBSD) 64 return Target.OS.DragonFlyBSD; 65 else 66 static assert(0, "unknown TARGET"); 67 } 68 69 ubyte defaultTargetOSMajor() 70 { 71 version (FreeBSD) 72 { 73 version (TARGET_FREEBSD10) 74 return 10; 75 else version (TARGET_FREEBSD11) 76 return 11; 77 else version (TARGET_FREEBSD12) 78 return 12; 79 else version (TARGET_FREEBSD13) 80 return 13; 81 else 82 return 0; 83 } 84 else 85 return 0; 86 } 87 88 /** 89 * Add default `version` identifier for dmd, and set the 90 * target platform in `params`. 91 * https://dlang.org/spec/version.html#predefined-versions 92 * 93 * Needs to be run after all arguments parsing (command line, DFLAGS environment 94 * variable and config file) in order to add final flags (such as `X86_64` or 95 * the `CRuntime` used). 96 * 97 * Params: 98 * params = which target to compile for (set by `setTarget()`) 99 * tgt = target 100 */ 101 public 102 void addDefaultVersionIdentifiers(const ref Param params, const ref Target tgt) 103 { 104 import dmd.cond : VersionCondition; 105 import dmd.dmdparams : driverParams, PIC; 106 107 VersionCondition.addPredefinedGlobalIdent("DigitalMars"); 108 VersionCondition.addPredefinedGlobalIdent("LittleEndian"); 109 VersionCondition.addPredefinedGlobalIdent("D_Version2"); 110 VersionCondition.addPredefinedGlobalIdent("all"); 111 112 addPredefinedGlobalIdentifiers(tgt); 113 114 if (params.ddoc.doOutput) 115 VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); 116 if (params.cov) 117 VersionCondition.addPredefinedGlobalIdent("D_Coverage"); 118 if (driverParams.pic != PIC.fixed) 119 VersionCondition.addPredefinedGlobalIdent(driverParams.pic == PIC.pic ? "D_PIC" : "D_PIE"); 120 if (params.useUnitTests) 121 VersionCondition.addPredefinedGlobalIdent("unittest"); 122 if (params.useAssert == CHECKENABLE.on) 123 VersionCondition.addPredefinedGlobalIdent("assert"); 124 if (params.useIn == CHECKENABLE.on) 125 VersionCondition.addPredefinedGlobalIdent("D_PreConditions"); 126 if (params.useOut == CHECKENABLE.on) 127 VersionCondition.addPredefinedGlobalIdent("D_PostConditions"); 128 if (params.useInvariants == CHECKENABLE.on) 129 VersionCondition.addPredefinedGlobalIdent("D_Invariants"); 130 if (params.useArrayBounds == CHECKENABLE.off) 131 VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); 132 if (params.betterC) 133 { 134 VersionCondition.addPredefinedGlobalIdent("D_BetterC"); 135 } 136 else 137 { 138 VersionCondition.addPredefinedGlobalIdent("D_ModuleInfo"); 139 VersionCondition.addPredefinedGlobalIdent("D_Exceptions"); 140 VersionCondition.addPredefinedGlobalIdent("D_TypeInfo"); 141 } 142 143 VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); 144 145 if (params.tracegc) 146 VersionCondition.addPredefinedGlobalIdent("D_ProfileGC"); 147 148 if (driverParams.optimize) 149 VersionCondition.addPredefinedGlobalIdent("D_Optimized"); 150 } 151 152 // /** 153 // * Add predefined global identifiers that are determied by the target 154 // */ 155 private 156 void addPredefinedGlobalIdentifiers(const ref Target tgt) 157 { 158 import dmd.cond : VersionCondition; 159 160 alias predef = VersionCondition.addPredefinedGlobalIdent; 161 if (tgt.cpu >= CPU.sse2) 162 { 163 predef("D_SIMD"); 164 if (tgt.cpu >= CPU.avx) 165 predef("D_AVX"); 166 if (tgt.cpu >= CPU.avx2) 167 predef("D_AVX2"); 168 } 169 170 with (Target) 171 { 172 if (tgt.os & OS.Posix) 173 predef("Posix"); 174 if (tgt.os & (OS.linux | OS.FreeBSD | OS.OpenBSD | OS.DragonFlyBSD | OS.Solaris)) 175 predef("ELFv1"); 176 switch (tgt.os) 177 { 178 case OS.none: { predef("FreeStanding"); break; } 179 case OS.linux: { predef("linux"); break; } 180 case OS.OpenBSD: { predef("OpenBSD"); break; } 181 case OS.DragonFlyBSD: { predef("DragonFlyBSD"); break; } 182 case OS.Solaris: { predef("Solaris"); break; } 183 case OS.Windows: 184 { 185 predef("Windows"); 186 VersionCondition.addPredefinedGlobalIdent(tgt.is64bit ? "Win64" : "Win32"); 187 break; 188 } 189 case OS.OSX: 190 { 191 predef("OSX"); 192 // For legacy compatibility 193 predef("darwin"); 194 break; 195 } 196 case OS.FreeBSD: 197 { 198 predef("FreeBSD"); 199 switch (tgt.osMajor) 200 { 201 case 10: predef("FreeBSD_10"); break; 202 case 11: predef("FreeBSD_11"); break; 203 case 12: predef("FreeBSD_12"); break; 204 case 13: predef("FreeBSD_13"); break; 205 default: predef("FreeBSD_11"); break; 206 } 207 break; 208 } 209 default: assert(0); 210 } 211 } 212 213 addCRuntimePredefinedGlobalIdent(tgt.c); 214 addCppRuntimePredefinedGlobalIdent(tgt.cpp); 215 216 if (tgt.is64bit) 217 { 218 VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); 219 VersionCondition.addPredefinedGlobalIdent("X86_64"); 220 } 221 else 222 { 223 VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy 224 VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); 225 VersionCondition.addPredefinedGlobalIdent("X86"); 226 } 227 if (tgt.isLP64) 228 VersionCondition.addPredefinedGlobalIdent("D_LP64"); 229 else if (tgt.is64bit) 230 VersionCondition.addPredefinedGlobalIdent("X32"); 231 } 232 233 private 234 void addCRuntimePredefinedGlobalIdent(const ref TargetC c) 235 { 236 import dmd.cond : VersionCondition; 237 238 alias predef = VersionCondition.addPredefinedGlobalIdent; 239 with (TargetC.Runtime) switch (c.runtime) 240 { 241 default: 242 case Unspecified: return; 243 case Bionic: return predef("CRuntime_Bionic"); 244 case DigitalMars: return predef("CRuntime_DigitalMars"); 245 case Glibc: return predef("CRuntime_Glibc"); 246 case Microsoft: return predef("CRuntime_Microsoft"); 247 case Musl: return predef("CRuntime_Musl"); 248 case Newlib: return predef("CRuntime_Newlib"); 249 case UClibc: return predef("CRuntime_UClibc"); 250 case WASI: return predef("CRuntime_WASI"); 251 } 252 } 253 254 private 255 void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) 256 { 257 import dmd.cond : VersionCondition; 258 259 alias predef = VersionCondition.addPredefinedGlobalIdent; 260 with (TargetCPP.Runtime) switch (cpp.runtime) 261 { 262 default: 263 case Unspecified: return; 264 case Clang: return predef("CppRuntime_Clang"); 265 case DigitalMars: return predef("CppRuntime_DigitalMars"); 266 case Gcc: return predef("CppRuntime_Gcc"); 267 case Microsoft: return predef("CppRuntime_Microsoft"); 268 case Sun: return predef("CppRuntime_Sun"); 269 } 270 } 271 272 //////////////////////////////////////////////////////////////////////////////// 273 /** 274 * Describes a back-end target. At present it is incomplete, but in the future 275 * it should grow to contain most or all target machine and target O/S specific 276 * information. 277 * 278 * In many cases, calls to sizeof() can't be used directly for getting data type 279 * sizes since cross compiling is supported and would end up using the host 280 * sizes rather than the target sizes. 281 */ 282 extern (C++) struct Target 283 { 284 import dmd.dscope : Scope; 285 import dmd.expression : Expression; 286 import dmd.func : FuncDeclaration; 287 import dmd.location; 288 import dmd.astenums : LINK, TY; 289 import dmd.mtype : Type, TypeFunction, TypeTuple; 290 import dmd.root.ctfloat : real_t; 291 import dmd.statement : Statement; 292 import dmd.tokens : EXP; 293 294 /// Bit decoding of the Target.OS 295 enum OS : ubyte 296 { 297 /* These are mutually exclusive; one and only one is set. 298 * Match spelling and casing of corresponding version identifiers 299 */ 300 none = 0, 301 linux = 1, 302 Windows = 2, 303 OSX = 4, 304 OpenBSD = 8, 305 FreeBSD = 0x10, 306 Solaris = 0x20, 307 DragonFlyBSD = 0x40, 308 309 // Combination masks 310 all = linux | Windows | OSX | OpenBSD | FreeBSD | Solaris | DragonFlyBSD, 311 Posix = linux | OSX | OpenBSD | FreeBSD | Solaris | DragonFlyBSD, 312 } 313 314 extern(D) enum ObjectFormat : ubyte 315 { 316 elf, 317 macho, 318 coff, 319 omf 320 } 321 322 OS os = defaultTargetOS(); 323 ubyte osMajor = defaultTargetOSMajor(); 324 325 // D ABI 326 ubyte ptrsize; /// size of a pointer in bytes 327 ubyte realsize; /// size a real consumes in memory 328 ubyte realpad; /// padding added to the CPU real size to bring it up to realsize 329 ubyte realalignsize; /// alignment for reals 330 ubyte classinfosize; /// size of `ClassInfo` 331 ulong maxStaticDataSize; /// maximum size of static data 332 333 /// C ABI 334 TargetC c; 335 336 /// C++ ABI 337 TargetCPP cpp; 338 339 /// Objective-C ABI 340 TargetObjC objc; 341 342 /// Architecture name 343 const(char)[] architectureName; 344 CPU cpu = CPU.baseline; // CPU instruction set to target 345 bool is64bit = (size_t.sizeof == 8); // generate 64 bit code for x86_64; true by default for 64 bit dmd 346 bool isLP64; // pointers are 64 bits 347 348 // Environmental 349 const(char)[] obj_ext; /// extension for object files 350 const(char)[] lib_ext; /// extension for static library files 351 const(char)[] dll_ext; /// extension for dynamic library files 352 bool run_noext; /// allow -run sources without extensions 353 bool omfobj = false; // for Win32: write OMF object files instead of MsCoff 354 /** 355 * Values representing all properties for floating point types 356 */ 357 extern (C++) struct FPTypeProperties(T) 358 { 359 real_t max; /// largest representable value that's not infinity 360 real_t min_normal; /// smallest representable normalized value that's not 0 361 real_t nan; /// NaN value 362 real_t infinity; /// infinity value 363 real_t epsilon; /// smallest increment to the value 1 364 365 long dig; /// number of decimal digits of precision 366 long mant_dig; /// number of bits in mantissa 367 long max_exp; /// maximum int value such that 2$(SUPERSCRIPT `max_exp-1`) is representable 368 long min_exp; /// minimum int value such that 2$(SUPERSCRIPT `min_exp-1`) is representable as a normalized value 369 long max_10_exp; /// maximum int value such that 10$(SUPERSCRIPT `max_10_exp` is representable) 370 long min_10_exp; /// minimum int value such that 10$(SUPERSCRIPT `min_10_exp`) is representable as a normalized value 371 372 extern (D) void initialize() 373 { 374 max = T.max; 375 min_normal = T.min_normal; 376 nan = T.nan; 377 infinity = T.infinity; 378 epsilon = T.epsilon; 379 dig = T.dig; 380 mant_dig = T.mant_dig; 381 max_exp = T.max_exp; 382 min_exp = T.min_exp; 383 max_10_exp = T.max_10_exp; 384 min_10_exp = T.min_10_exp; 385 } 386 } 387 388 FPTypeProperties!float FloatProperties; /// 389 FPTypeProperties!double DoubleProperties; /// 390 FPTypeProperties!real_t RealProperties; /// 391 392 private Type tvalist; // cached lazy result of va_listType() 393 394 private const(Param)* params; // cached reference to global.params 395 396 /** 397 * Initialize the Target 398 */ 399 extern (C++) void _init(ref const Param params) 400 { 401 // is64bit, omfobj and cpu are initialized in parseCommandLine 402 403 this.params = ¶ms; 404 405 FloatProperties.initialize(); 406 DoubleProperties.initialize(); 407 RealProperties.initialize(); 408 409 isLP64 = is64bit; 410 411 // These have default values for 32 bit code, they get 412 // adjusted for 64 bit code. 413 ptrsize = 4; 414 classinfosize = 0x4C; // 76 415 416 /* gcc uses int.max for 32 bit compilations, and long.max for 64 bit ones. 417 * Set to int.max for both, because the rest of the compiler cannot handle 418 * 2^64-1 without some pervasive rework. The trouble is that much of the 419 * front and back end uses 32 bit ints for sizes and offsets. Since C++ 420 * silently truncates 64 bit ints to 32, finding all these dependencies will be a problem. 421 */ 422 maxStaticDataSize = int.max; 423 424 if (isLP64) 425 { 426 ptrsize = 8; 427 classinfosize = 0x98; // 152 428 } 429 if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) 430 { 431 realsize = 12; 432 realpad = 2; 433 realalignsize = 4; 434 } 435 else if (os == Target.OS.OSX) 436 { 437 realsize = 16; 438 realpad = 6; 439 realalignsize = 16; 440 } 441 else if (os == Target.OS.Windows) 442 { 443 realsize = 10; 444 realpad = 0; 445 realalignsize = 2; 446 if (ptrsize == 4) 447 { 448 /* Optlink cannot deal with individual data chunks 449 * larger than 16Mb 450 */ 451 maxStaticDataSize = 0x100_0000; // 16Mb 452 } 453 } 454 else 455 assert(0); 456 if (is64bit) 457 { 458 if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) 459 { 460 realsize = 16; 461 realpad = 6; 462 realalignsize = 16; 463 } 464 } 465 466 c.initialize(params, this); 467 cpp.initialize(params, this); 468 objc.initialize(params, this); 469 470 if (is64bit) 471 architectureName = "X86_64"; 472 else 473 architectureName = "X86"; 474 475 if (os == Target.OS.Windows) 476 { 477 obj_ext = "obj"; 478 lib_ext = "lib"; 479 dll_ext = "dll"; 480 run_noext = false; 481 } 482 else if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris | Target.OS.OSX)) 483 { 484 obj_ext = "o"; 485 lib_ext = "a"; 486 if (os == Target.OS.OSX) 487 dll_ext = "dylib"; 488 else 489 dll_ext = "so"; 490 run_noext = true; 491 } 492 else 493 assert(0, "unknown environment"); 494 } 495 496 /** 497 Determine the object format to be used 498 */ 499 extern(D) Target.ObjectFormat objectFormat() 500 { 501 if (os == Target.OS.OSX) 502 return Target.ObjectFormat.macho; 503 else if (os & Target.OS.Posix) 504 return Target.ObjectFormat.elf; 505 else if (os == Target.OS.Windows) 506 return omfobj ? Target.ObjectFormat.omf : Target.ObjectFormat.coff; 507 else 508 assert(0, "unkown object format"); 509 } 510 511 /** 512 * Determine the instruction set to be used 513 */ 514 void setCPU() 515 { 516 if(!isXmmSupported()) 517 { 518 cpu = CPU.x87; // cannot support other instruction sets 519 return; 520 } 521 switch (cpu) 522 { 523 case CPU.baseline: 524 cpu = CPU.sse2; 525 break; 526 527 case CPU.native: 528 { 529 import core.cpuid; 530 cpu = core.cpuid.avx2 ? CPU.avx2 : 531 core.cpuid.avx ? CPU.avx : 532 CPU.sse2; 533 break; 534 } 535 default: 536 break; 537 } 538 } 539 540 /** 541 * Deinitializes the global state of the compiler. 542 * 543 * This can be used to restore the state set by `_init` to its original 544 * state. 545 */ 546 void deinitialize() 547 { 548 this = this.init; 549 } 550 551 /** 552 * Requested target memory alignment size of the given type. 553 * Params: 554 * type = type to inspect 555 * Returns: 556 * alignment in bytes 557 */ 558 extern (C++) uint alignsize(Type type) 559 { 560 assert(type.isTypeBasic()); 561 switch (type.ty) 562 { 563 case TY.Tfloat80: 564 case TY.Timaginary80: 565 case TY.Tcomplex80: 566 return target.realalignsize; 567 case TY.Tcomplex32: 568 if (os & Target.OS.Posix) 569 return 4; 570 break; 571 case TY.Tint64: 572 case TY.Tuns64: 573 case TY.Tfloat64: 574 case TY.Timaginary64: 575 case TY.Tcomplex64: 576 if (os & Target.OS.Posix) 577 return is64bit ? 8 : 4; 578 break; 579 default: 580 break; 581 } 582 return cast(uint)type.size(Loc.initial); 583 } 584 585 /** 586 * Requested target field alignment size of the given type. 587 * Params: 588 * type = type to inspect 589 * Returns: 590 * alignment in bytes 591 */ 592 extern (C++) uint fieldalign(Type type) 593 { 594 const size = type.alignsize(); 595 596 if ((is64bit || os == Target.OS.OSX) && (size == 16 || size == 32)) 597 return size; 598 599 return (8 < size) ? 8 : size; 600 } 601 602 /** 603 * Type for the `va_list` type for the target; e.g., required for `_argptr` 604 * declarations. 605 * NOTE: For Posix/x86_64 this returns the type which will really 606 * be used for passing an argument of type va_list. 607 * Returns: 608 * `Type` that represents `va_list`. 609 */ 610 extern (C++) Type va_listType(const ref Loc loc, Scope* sc) 611 { 612 if (tvalist) 613 return tvalist; 614 615 if (os == Target.OS.Windows) 616 { 617 tvalist = Type.tchar.pointerTo(); 618 } 619 else if (os & Target.OS.Posix) 620 { 621 if (is64bit) 622 { 623 import dmd.identifier : Identifier; 624 import dmd.mtype : TypeIdentifier; 625 import dmd.typesem : typeSemantic; 626 tvalist = new TypeIdentifier(Loc.initial, Identifier.idPool("__va_list_tag")).pointerTo(); 627 tvalist = typeSemantic(tvalist, loc, sc); 628 } 629 else 630 { 631 tvalist = Type.tchar.pointerTo(); 632 } 633 } 634 else 635 { 636 assert(0); 637 } 638 639 return tvalist; 640 } 641 642 /** 643 * Checks whether the target supports a vector type. 644 * Params: 645 * sz = vector type size in bytes 646 * type = vector element type 647 * Returns: 648 * 0 vector type is supported, 649 * 1 vector type is not supported on the target at all 650 * 2 vector element type is not supported 651 * 3 vector size is not supported 652 */ 653 extern (C++) int isVectorTypeSupported(int sz, Type type) 654 { 655 if (!isXmmSupported()) 656 return 1; // not supported 657 658 switch (type.ty) 659 { 660 case TY.Tvoid: 661 case TY.Tint8: 662 case TY.Tuns8: 663 case TY.Tint16: 664 case TY.Tuns16: 665 case TY.Tint32: 666 case TY.Tuns32: 667 case TY.Tfloat32: 668 case TY.Tint64: 669 case TY.Tuns64: 670 case TY.Tfloat64: 671 break; 672 default: 673 return 2; // wrong base type 674 } 675 676 // Whether a vector is really supported depends on the CPU being targeted. 677 if (sz == 16) 678 { 679 switch (type.ty) 680 { 681 case TY.Tint32: 682 case TY.Tuns32: 683 case TY.Tfloat32: 684 if (cpu < CPU.sse) 685 return 3; // no SSE vector support 686 break; 687 688 case TY.Tvoid: 689 case TY.Tint8: 690 case TY.Tuns8: 691 case TY.Tint16: 692 case TY.Tuns16: 693 case TY.Tint64: 694 case TY.Tuns64: 695 case TY.Tfloat64: 696 if (cpu < CPU.sse2) 697 return 3; // no SSE2 vector support 698 break; 699 700 default: 701 assert(0); 702 } 703 } 704 else if (sz == 32) 705 { 706 if (cpu < CPU.avx) 707 return 3; // no AVX vector support 708 } 709 else 710 return 3; // wrong size 711 712 return 0; 713 } 714 715 /** 716 * Checks whether the target supports the given operation for vectors. 717 * Params: 718 * type = target type of operation 719 * op = the unary or binary op being done on the `type` 720 * t2 = type of second operand if `op` is a binary operation 721 * Returns: 722 * true if the operation is supported or type is not a vector 723 */ 724 extern (C++) bool isVectorOpSupported(Type type, EXP op, Type t2 = null) 725 { 726 import dmd.hdrgen : EXPtoString; 727 728 auto tvec = type.isTypeVector(); 729 if (tvec is null) 730 return true; // not a vector op 731 const vecsize = cast(int)tvec.basetype.size(); 732 const elemty = cast(int)tvec.elementType().ty; 733 734 // Only operations on these sizes are supported (see isVectorTypeSupported) 735 if (vecsize != 16 && vecsize != 32) 736 return false; 737 738 bool supported = false; 739 switch (op) 740 { 741 case EXP.uadd: 742 // Expression is a no-op, supported everywhere. 743 supported = tvec.isscalar(); 744 break; 745 746 case EXP.negate: 747 if (vecsize == 16) 748 { 749 // float[4] negate needs SSE support ({V}SUBPS) 750 if (elemty == TY.Tfloat32 && cpu >= CPU.sse) 751 supported = true; 752 // double[2] negate needs SSE2 support ({V}SUBPD) 753 else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) 754 supported = true; 755 // (u)byte[16]/short[8]/int[4]/long[2] negate needs SSE2 support ({V}PSUB[BWDQ]) 756 else if (tvec.isintegral() && cpu >= CPU.sse2) 757 supported = true; 758 } 759 else if (vecsize == 32) 760 { 761 // float[8]/double[4] negate needs AVX support (VSUBP[SD]) 762 if (tvec.isfloating() && cpu >= CPU.avx) 763 supported = true; 764 // (u)byte[32]/short[16]/int[8]/long[4] negate needs AVX2 support (VPSUB[BWDQ]) 765 else if (tvec.isintegral() && cpu >= CPU.avx2) 766 supported = true; 767 } 768 break; 769 770 case EXP.identity, EXP.notIdentity: 771 supported = false; 772 break; 773 774 case EXP.lessThan, EXP.greaterThan, EXP.lessOrEqual, EXP.greaterOrEqual: 775 case EXP.equal: 776 case EXP.notEqual: 777 if (vecsize == 16) 778 { 779 // float[4] comparison needs SSE support (CMP{EQ,NEQ,LT,LE}PS) 780 if (elemty == TY.Tfloat32 && cpu >= CPU.sse) 781 supported = true; 782 // double[2] comparison needs SSE2 support (CMP{EQ,NEQ,LT,LE}PD) 783 else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) 784 supported = true; 785 else if (tvec.isintegral()) 786 { 787 if (elemty == TY.Tint64 || elemty == TY.Tuns64) 788 { 789 // (u)long[2] equality needs SSE4.1 support (PCMPEQQ) 790 if ((op == EXP.equal || op == EXP.notEqual) && cpu >= CPU.sse4_1) 791 supported = true; 792 // (u)long[2] comparison needs SSE4.2 support (PCMPGTQ) 793 else if (cpu >= CPU.sse4_2) 794 supported = true; 795 } 796 // (u)byte[16]/short[8]/int[4] comparison needs SSE2 support (PCMP{EQ,GT}[BWD]) 797 else if (cpu >= CPU.sse2) 798 supported = true; 799 } 800 } 801 else if (vecsize == 32) 802 { 803 // float[8]/double[4] comparison needs AVX support (VCMP{EQ,NEQ,LT,LE}P[SD]) 804 if (tvec.isfloating() && cpu >= CPU.avx) 805 supported = true; 806 // (u)byte[32]/short[16]/int[8]/long[4] comparison needs AVX2 support (VPCMP{EQ,GT}[BWDQ]) 807 else if (tvec.isintegral() && cpu >= CPU.avx2) 808 supported = true; 809 } 810 break; 811 812 case EXP.leftShift, EXP.leftShiftAssign, EXP.rightShift, EXP.rightShiftAssign, EXP.unsignedRightShift, EXP.unsignedRightShiftAssign: 813 supported = false; 814 break; 815 816 case EXP.add, EXP.addAssign, EXP.min, EXP.minAssign: 817 if (vecsize == 16) 818 { 819 // float[4] add/sub needs SSE support ({V}ADDPS, {V}SUBPS) 820 if (elemty == TY.Tfloat32 && cpu >= CPU.sse) 821 supported = true; 822 // double[2] add/sub needs SSE2 support ({V}ADDPD, {V}SUBPD) 823 else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) 824 supported = true; 825 // (u)byte[16]/short[8]/int[4]/long[2] add/sub needs SSE2 support ({V}PADD[BWDQ], {V}PSUB[BWDQ]) 826 else if (tvec.isintegral() && cpu >= CPU.sse2) 827 supported = true; 828 } 829 else if (vecsize == 32) 830 { 831 // float[8]/double[4] add/sub needs AVX support (VADDP[SD], VSUBP[SD]) 832 if (tvec.isfloating() && cpu >= CPU.avx) 833 supported = true; 834 // (u)byte[32]/short[16]/int[8]/long[4] add/sub needs AVX2 support (VPADD[BWDQ], VPSUB[BWDQ]) 835 else if (tvec.isintegral() && cpu >= CPU.avx2) 836 supported = true; 837 } 838 break; 839 840 case EXP.mul, EXP.mulAssign: 841 if (vecsize == 16) 842 { 843 // float[4] multiply needs SSE support ({V}MULPS) 844 if (elemty == TY.Tfloat32 && cpu >= CPU.sse) 845 supported = true; 846 // double[2] multiply needs SSE2 support ({V}MULPD) 847 else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) 848 supported = true; 849 // (u)short[8] multiply needs SSE2 support ({V}PMULLW) 850 else if ((elemty == TY.Tint16 || elemty == TY.Tuns16) && cpu >= CPU.sse2) 851 supported = true; 852 // (u)int[4] multiply needs SSE4.1 support ({V}PMULLD) 853 else if ((elemty == TY.Tint32 || elemty == TY.Tuns32) && cpu >= CPU.sse4_1) 854 supported = true; 855 } 856 else if (vecsize == 32) 857 { 858 // float[8]/double[4] multiply needs AVX support (VMULP[SD]) 859 if (tvec.isfloating() && cpu >= CPU.avx) 860 supported = true; 861 // (u)short[16] multiply needs AVX2 support (VPMULLW) 862 else if ((elemty == TY.Tint16 || elemty == TY.Tuns16) && cpu >= CPU.avx2) 863 supported = true; 864 // (u)int[8] multiply needs AVX2 support (VPMULLD) 865 else if ((elemty == TY.Tint32 || elemty == TY.Tuns32) && cpu >= CPU.avx2) 866 supported = true; 867 } 868 break; 869 870 case EXP.div, EXP.divAssign: 871 if (vecsize == 16) 872 { 873 // float[4] divide needs SSE support ({V}DIVPS) 874 if (elemty == TY.Tfloat32 && cpu >= CPU.sse) 875 supported = true; 876 // double[2] divide needs SSE2 support ({V}DIVPD) 877 else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) 878 supported = true; 879 } 880 else if (vecsize == 32) 881 { 882 // float[8]/double[4] multiply needs AVX support (VDIVP[SD]) 883 if (tvec.isfloating() && cpu >= CPU.avx) 884 supported = true; 885 } 886 break; 887 888 case EXP.mod, EXP.modAssign: 889 supported = false; 890 break; 891 892 case EXP.and, EXP.andAssign, EXP.or, EXP.orAssign, EXP.xor, EXP.xorAssign: 893 // (u)byte[16]/short[8]/int[4]/long[2] bitwise ops needs SSE2 support ({V}PAND, {V}POR, {V}PXOR) 894 if (vecsize == 16 && tvec.isintegral() && cpu >= CPU.sse2) 895 supported = true; 896 // (u)byte[32]/short[16]/int[8]/long[4] bitwise ops needs AVX2 support (VPAND, VPOR, VPXOR) 897 else if (vecsize == 32 && tvec.isintegral() && cpu >= CPU.avx2) 898 supported = true; 899 break; 900 901 case EXP.not: 902 supported = false; 903 break; 904 905 case EXP.tilde: 906 // (u)byte[16]/short[8]/int[4]/long[2] logical exclusive needs SSE2 support ({V}PXOR) 907 if (vecsize == 16 && tvec.isintegral() && cpu >= CPU.sse2) 908 supported = true; 909 // (u)byte[32]/short[16]/int[8]/long[4] logical exclusive needs AVX2 support (VPXOR) 910 else if (vecsize == 32 && tvec.isintegral() && cpu >= CPU.avx2) 911 supported = true; 912 break; 913 914 case EXP.pow, EXP.powAssign: 915 supported = false; 916 break; 917 918 default: 919 // import std.stdio : stderr, writeln; 920 // stderr.writeln(op); 921 assert(0, "unhandled op " ~ EXPtoString(cast(EXP)op)); 922 } 923 return supported; 924 } 925 926 /** 927 * Default system linkage for the target. 928 * Returns: 929 * `LINK` to use for `extern(System)` 930 */ 931 extern (C++) LINK systemLinkage() 932 { 933 return os == Target.OS.Windows ? LINK.windows : LINK.c; 934 } 935 936 /** 937 * Describes how an argument type is passed to a function on target. 938 * Params: 939 * t = type to break down 940 * Returns: 941 * tuple of types if type is passed in one or more registers 942 * empty tuple if type is always passed on the stack 943 * null if the type is a `void` or argtypes aren't supported by the target 944 */ 945 extern (C++) TypeTuple toArgTypes(Type t) 946 { 947 import dmd.argtypes_x86 : toArgTypes_x86; 948 import dmd.argtypes_sysv_x64 : toArgTypes_sysv_x64; 949 if (is64bit) 950 { 951 // no argTypes for Win64 yet 952 return isPOSIX ? toArgTypes_sysv_x64(t) : null; 953 } 954 return toArgTypes_x86(t); 955 } 956 957 /** 958 * Determine return style of function - whether in registers or 959 * through a hidden pointer to the caller's stack. 960 * Params: 961 * tf = function type to check 962 * needsThis = true if the function type is for a non-static member function 963 * Returns: 964 * true if return value from function is on the stack 965 */ 966 extern (C++) bool isReturnOnStack(TypeFunction tf, bool needsThis) 967 { 968 import dmd.id : Id; 969 import dmd.argtypes_sysv_x64 : toArgTypes_sysv_x64; 970 971 if (tf.isref) 972 { 973 //printf(" ref false\n"); 974 return false; // returns a pointer 975 } 976 977 Type tn = tf.next; 978 if (auto te = tn.isTypeEnum()) 979 { 980 if (te.sym.isSpecial()) 981 { 982 // Special enums with target-specific return style 983 if (te.sym.ident == Id.__c_complex_float) 984 tn = Type.tcomplex32.castMod(tn.mod); 985 else if (te.sym.ident == Id.__c_complex_double) 986 tn = Type.tcomplex64.castMod(tn.mod); 987 else if (te.sym.ident == Id.__c_complex_real) 988 tn = Type.tcomplex80.castMod(tn.mod); 989 } 990 } 991 tn = tn.toBasetype(); 992 //printf("tn = %s\n", tn.toChars()); 993 const sz = tn.size(); 994 Type tns = tn; 995 996 if (os == Target.OS.Windows && is64bit) 997 { 998 // https://msdn.microsoft.com/en-us/library/7572ztz4%28v=vs.100%29.aspx 999 if (tns.ty == TY.Tcomplex32) 1000 return true; 1001 if (tns.isscalar()) 1002 return false; 1003 1004 tns = tns.baseElemOf(); 1005 if (auto ts = tns.isTypeStruct()) 1006 { 1007 auto sd = ts.sym; 1008 if (tf.linkage == LINK.cpp && needsThis) 1009 return true; 1010 if (!sd.isPOD() || sz > 8) 1011 return true; 1012 if (sd.fields.length == 0) 1013 return true; 1014 } 1015 if (sz <= 16 && !(sz & (sz - 1))) 1016 return false; 1017 return true; 1018 } 1019 else if (os == Target.OS.Windows) 1020 { 1021 Type tb = tns.baseElemOf(); 1022 if (tb.ty == TY.Tstruct) 1023 { 1024 if (tf.linkage == LINK.cpp && needsThis) 1025 return true; 1026 } 1027 } 1028 else if (is64bit && isPOSIX) 1029 { 1030 TypeTuple tt = toArgTypes_sysv_x64(tn); 1031 if (!tt) 1032 return false; // void 1033 else 1034 return !tt.arguments.length; 1035 } 1036 1037 Lagain: 1038 if (tns.ty == TY.Tsarray) 1039 { 1040 tns = tns.baseElemOf(); 1041 if (tns.ty != TY.Tstruct) 1042 { 1043 L2: 1044 if (os == Target.OS.linux && tf.linkage != LINK.d && !is64bit) 1045 { 1046 // 32 bit C/C++ structs always on stack 1047 } 1048 else 1049 { 1050 switch (sz) 1051 { 1052 case 1: 1053 case 2: 1054 case 4: 1055 case 8: 1056 //printf(" sarray false\n"); 1057 return false; // return small structs in regs 1058 // (not 3 byte structs!) 1059 default: 1060 break; 1061 } 1062 } 1063 //printf(" sarray true\n"); 1064 return true; 1065 } 1066 } 1067 1068 if (auto ts = tns.isTypeStruct()) 1069 { 1070 auto sd = ts.sym; 1071 if (os == Target.OS.linux && tf.linkage != LINK.d && !is64bit) 1072 { 1073 //printf(" 2 true\n"); 1074 return true; // 32 bit C/C++ structs always on stack 1075 } 1076 if (os == Target.OS.Windows && tf.linkage == LINK.cpp && !is64bit && 1077 sd.isPOD() && sd.ctor) 1078 { 1079 // win32 returns otherwise POD structs with ctors via memory 1080 return true; 1081 } 1082 if (sd.numArgTypes() == 1) 1083 { 1084 tns = sd.argType(0); 1085 if (tns.ty != TY.Tstruct) 1086 goto L2; 1087 goto Lagain; 1088 } 1089 else if (is64bit && sd.numArgTypes() == 0) 1090 return true; 1091 else if (sd.isPOD()) 1092 { 1093 switch (sz) 1094 { 1095 case 1: 1096 case 2: 1097 case 4: 1098 case 8: 1099 //printf(" 3 false\n"); 1100 return false; // return small structs in regs 1101 // (not 3 byte structs!) 1102 case 16: 1103 if (os & Target.OS.Posix && is64bit) 1104 return false; 1105 break; 1106 1107 default: 1108 break; 1109 } 1110 } 1111 //printf(" 3 true\n"); 1112 return true; 1113 } 1114 else if (os & Target.OS.Posix && 1115 (tf.linkage == LINK.c || tf.linkage == LINK.cpp) && 1116 tns.iscomplex()) 1117 { 1118 if (tns.ty == TY.Tcomplex32) 1119 return false; // in EDX:EAX, not ST1:ST0 1120 else 1121 return true; 1122 } 1123 else if (os == Target.OS.Windows && 1124 !is64bit && 1125 tf.linkage == LINK.cpp && 1126 tf.isfloating()) 1127 { 1128 /* See DMC++ function exp2_retmethod() 1129 * https://github.com/DigitalMars/Compiler/blob/master/dm/src/dmc/dexp2.d#L149 1130 */ 1131 return true; 1132 } 1133 else 1134 { 1135 //assert(sz <= 16); 1136 //printf(" 4 false\n"); 1137 return false; 1138 } 1139 } 1140 1141 /** 1142 * Decides whether an `in` parameter of the specified POD type is to be 1143 * passed by reference or by value. To be used with `-preview=in` only! 1144 * Params: 1145 * t = type of the `in` parameter, must be a POD 1146 * Returns: 1147 * `true` if the `in` parameter is to be passed by reference 1148 */ 1149 extern (C++) bool preferPassByRef(Type t) 1150 { 1151 const size = t.size(); 1152 if (is64bit) 1153 { 1154 if (os == Target.OS.Windows) 1155 { 1156 // Win64 special case: by-value for slices and delegates due to 1157 // high number of usages in druntime/Phobos (compiled without 1158 // -preview=in but supposed to link against -preview=in code) 1159 const ty = t.toBasetype().ty; 1160 if (ty == TY.Tarray || ty == TY.Tdelegate) 1161 return false; 1162 1163 // If size is larger than 8 or not a power-of-2, the Win64 ABI 1164 // would require a hidden reference anyway. 1165 return size > 8 1166 || (size > 0 && (size & (size - 1)) != 0); 1167 } 1168 else // SysV x86_64 ABI 1169 { 1170 // Prefer a ref if the POD cannot be passed in registers, i.e., 1171 // would be passed on the stack, *and* the size is > 16. 1172 if (size <= 16) 1173 return false; 1174 1175 TypeTuple getArgTypes() 1176 { 1177 import dmd.astenums : Sizeok; 1178 if (auto ts = t.toBasetype().isTypeStruct()) 1179 { 1180 auto sd = ts.sym; 1181 assert(sd.sizeok == Sizeok.done); 1182 return sd.argTypes; 1183 } 1184 return toArgTypes(t); 1185 } 1186 1187 TypeTuple argTypes = getArgTypes(); 1188 assert(argTypes !is null, "size == 0 should already be handled"); 1189 return argTypes.arguments.length == 0; // cannot be passed in registers 1190 } 1191 } 1192 else // 32-bit x86 ABI 1193 { 1194 // Prefer a ref if the size is > 2 machine words. 1195 return size > 8; 1196 } 1197 } 1198 1199 // this guarantees `getTargetInfo` and `allTargetInfos` remain in sync 1200 private enum TargetInfoKeys 1201 { 1202 cppRuntimeLibrary, 1203 cppStd, 1204 floatAbi, 1205 objectFormat, 1206 } 1207 1208 /** 1209 * Get targetInfo by key 1210 * Params: 1211 * name = name of targetInfo to get 1212 * loc = location to use for error messages 1213 * Returns: 1214 * Expression for the requested targetInfo 1215 */ 1216 extern (C++) Expression getTargetInfo(const(char)* name, const ref Loc loc) 1217 { 1218 import dmd.dmdparams : driverParams; 1219 import dmd.expression : IntegerExp, StringExp; 1220 import dmd.root.string : toDString; 1221 1222 StringExp stringExp(const(char)[] sval) 1223 { 1224 return new StringExp(loc, sval); 1225 } 1226 1227 switch (name.toDString) with (TargetInfoKeys) 1228 { 1229 case objectFormat.stringof: 1230 if (os == Target.OS.Windows) 1231 return stringExp(omfobj ? "omf" : "coff" ); 1232 else if (os == Target.OS.OSX) 1233 return stringExp("macho"); 1234 else 1235 return stringExp("elf"); 1236 case floatAbi.stringof: 1237 return stringExp("hard"); 1238 case cppRuntimeLibrary.stringof: 1239 if (os == Target.OS.Windows) 1240 { 1241 if (omfobj) 1242 return stringExp("snn"); 1243 return stringExp(driverParams.mscrtlib); 1244 } 1245 return stringExp(""); 1246 case cppStd.stringof: 1247 return new IntegerExp(params.cplusplus); 1248 1249 default: 1250 return null; 1251 } 1252 } 1253 1254 /** 1255 * Params: 1256 * tf = type of function being called 1257 * Returns: `true` if the callee invokes destructors for arguments. 1258 */ 1259 extern (C++) bool isCalleeDestroyingArgs(TypeFunction tf) 1260 { 1261 // On windows, the callee destroys arguments always regardless of function linkage, 1262 // and regardless of whether the caller or callee cleans the stack. 1263 return os == Target.OS.Windows || 1264 // C++ on non-Windows platforms has the caller destroying the arguments 1265 tf.linkage != LINK.cpp; 1266 } 1267 1268 /** 1269 * Returns true if the implementation for object monitors is always defined 1270 * in the D runtime library (rt/monitor_.d). 1271 * Params: 1272 * fd = function with `synchronized` storage class. 1273 * fbody = entire function body of `fd` 1274 * Returns: 1275 * `false` if the target backend handles synchronizing monitors. 1276 */ 1277 extern (C++) bool libraryObjectMonitors(FuncDeclaration fd, Statement fbody) 1278 { 1279 if (!is64bit && os == Target.OS.Windows && !fd.isStatic() && !fbody.usesEH() && !params.trace) 1280 { 1281 /* The back end uses the "jmonitor" hack for syncing; 1282 * no need to do the sync in the library. 1283 */ 1284 return false; 1285 } 1286 return true; 1287 } 1288 1289 /** 1290 * Returns true if the target supports `pragma(linkerDirective)`. 1291 * Returns: 1292 * `false` if the target does not support `pragma(linkerDirective)`. 1293 */ 1294 extern (C++) bool supportsLinkerDirective() const 1295 { 1296 return os == Target.OS.Windows && !omfobj; 1297 } 1298 1299 //////////////////////////////////////////////////////////////////////////// 1300 /* All functions after this point are extern (D), as they are only relevant 1301 * for targets of DMD, and should not be used in front-end code. 1302 */ 1303 1304 /****************** 1305 * Returns: 1306 * true if xmm usage is supported 1307 */ 1308 extern (D) bool isXmmSupported() 1309 { 1310 return is64bit || os == Target.OS.OSX; 1311 } 1312 1313 /** 1314 * Returns: 1315 * true if generating code for POSIX 1316 */ 1317 extern (D) @property bool isPOSIX() scope const nothrow @nogc 1318 out(result) { assert(result || os == Target.OS.Windows); } 1319 do 1320 { 1321 return (os & Target.OS.Posix) != 0; 1322 } 1323 1324 /********************* 1325 * Returns: 1326 * alignment of the stack 1327 */ 1328 extern (D) uint stackAlign() 1329 { 1330 return isXmmSupported() ? 16 : (is64bit ? 8 : 4); 1331 } 1332 } 1333 1334 //////////////////////////////////////////////////////////////////////////////// 1335 /** 1336 * Functions and variables specific to interfacing with extern(C) ABI. 1337 */ 1338 struct TargetC 1339 { 1340 enum Runtime : ubyte 1341 { 1342 Unspecified, 1343 Bionic, 1344 DigitalMars, 1345 Glibc, 1346 Microsoft, 1347 Musl, 1348 Newlib, 1349 UClibc, 1350 WASI, 1351 } 1352 1353 enum BitFieldStyle : ubyte 1354 { 1355 Unspecified, 1356 DM, /// Digital Mars 32 bit C compiler 1357 MS, /// Microsoft 32 and 64 bit C compilers 1358 /// https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160 1359 /// https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160 1360 Gcc_Clang, /// gcc and clang 1361 } 1362 bool crtDestructorsSupported = true; /// Not all platforms support crt_destructor 1363 ubyte boolsize; /// size of a C `_Bool` type 1364 ubyte shortsize; /// size of a C `short` or `unsigned short` type 1365 ubyte intsize; /// size of a C `int` or `unsigned int` type 1366 ubyte longsize; /// size of a C `long` or `unsigned long` type 1367 ubyte long_longsize; /// size of a C `long long` or `unsigned long long` type 1368 ubyte long_doublesize; /// size of a C `long double` 1369 ubyte wchar_tsize; /// size of a C `wchar_t` type 1370 Runtime runtime; /// vendor of the C runtime to link against 1371 BitFieldStyle bitFieldStyle; /// different C compilers do it differently 1372 1373 extern (D) void initialize(ref const Param params, ref const Target target) 1374 { 1375 const os = target.os; 1376 boolsize = 1; 1377 shortsize = 2; 1378 intsize = 4; 1379 long_longsize = 8; 1380 if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) 1381 longsize = 4; 1382 else if (os == Target.OS.OSX) 1383 longsize = 4; 1384 else if (os == Target.OS.Windows) 1385 longsize = 4; 1386 else 1387 assert(0); 1388 if (target.is64bit) 1389 { 1390 if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) 1391 longsize = 8; 1392 else if (os == Target.OS.OSX) 1393 longsize = 8; 1394 } 1395 if (target.is64bit && os == Target.OS.Windows) 1396 long_doublesize = 8; 1397 else 1398 long_doublesize = target.realsize; 1399 if (os == Target.OS.Windows) 1400 wchar_tsize = 2; 1401 else 1402 wchar_tsize = 4; 1403 1404 if (os == Target.OS.Windows) 1405 runtime = target.omfobj ? Runtime.DigitalMars : Runtime.Microsoft; 1406 else if (os == Target.OS.linux) 1407 { 1408 // Note: This is overridden later by `-target=<triple>` if supplied. 1409 // For now, choose the sensible default. 1410 version (CRuntime_Musl) 1411 runtime = Runtime.Musl; 1412 else 1413 runtime = Runtime.Glibc; 1414 } 1415 1416 if (os == Target.OS.Windows) 1417 bitFieldStyle = target.omfobj ? BitFieldStyle.DM : BitFieldStyle.MS; 1418 else if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OSX | 1419 Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) 1420 bitFieldStyle = BitFieldStyle.Gcc_Clang; 1421 else 1422 assert(0); 1423 /* 1424 MacOS Monterey (12) does not support C runtime destructors. 1425 */ 1426 if (os == Target.OS.OSX) 1427 { 1428 crtDestructorsSupported = false; 1429 } 1430 } 1431 } 1432 1433 //////////////////////////////////////////////////////////////////////////////// 1434 /** 1435 * Functions and variables specific to interface with extern(C++) ABI. 1436 */ 1437 struct TargetCPP 1438 { 1439 import dmd.dsymbol : Dsymbol; 1440 import dmd.dclass : ClassDeclaration; 1441 import dmd.func : FuncDeclaration; 1442 import dmd.mtype : Type; 1443 1444 enum Runtime : ubyte 1445 { 1446 Unspecified, 1447 Clang, 1448 DigitalMars, 1449 Gcc, 1450 Microsoft, 1451 Sun 1452 } 1453 bool reverseOverloads; /// set if overloaded functions are grouped and in reverse order (such as in dmc and cl) 1454 bool exceptions; /// set if catching C++ exceptions is supported 1455 bool twoDtorInVtable; /// target C++ ABI puts deleting and non-deleting destructor into vtable 1456 bool splitVBasetable; /// set if C++ ABI uses separate tables for virtual functions and virtual bases 1457 bool wrapDtorInExternD; /// set if C++ dtors require a D wrapper to be callable from runtime 1458 Runtime runtime; /// vendor of the C++ runtime to link against 1459 1460 extern (D) void initialize(ref const Param params, ref const Target target) 1461 { 1462 const os = target.os; 1463 if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) 1464 twoDtorInVtable = true; 1465 else if (os == Target.OS.OSX) 1466 twoDtorInVtable = true; 1467 else if (os == Target.OS.Windows) 1468 { 1469 reverseOverloads = true; 1470 splitVBasetable = !target.omfobj; 1471 } 1472 else 1473 assert(0); 1474 exceptions = (os & Target.OS.Posix) != 0; 1475 if (os == Target.OS.Windows) 1476 runtime = target.omfobj ? Runtime.DigitalMars : Runtime.Microsoft; 1477 else if (os & (Target.OS.linux | Target.OS.DragonFlyBSD)) 1478 runtime = Runtime.Gcc; 1479 else if (os & (Target.OS.OSX | Target.OS.FreeBSD | Target.OS.OpenBSD)) 1480 runtime = Runtime.Clang; 1481 else if (os == Target.OS.Solaris) 1482 runtime = Runtime.Gcc; 1483 else 1484 assert(0); 1485 // C++ and D ABI incompatible on all (?) x86 32-bit platforms 1486 wrapDtorInExternD = !target.is64bit; 1487 } 1488 1489 /** 1490 * Mangle the given symbol for C++ ABI. 1491 * Params: 1492 * s = declaration with C++ linkage 1493 * Returns: 1494 * string mangling of symbol 1495 */ 1496 extern (C++) const(char)* toMangle(Dsymbol s) 1497 { 1498 import dmd.cppmangle : toCppMangleItanium; 1499 import dmd.cppmanglewin : toCppMangleDMC, toCppMangleMSVC; 1500 1501 if (target.os & (Target.OS.linux | Target.OS.OSX | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD)) 1502 return toCppMangleItanium(s); 1503 if (target.os == Target.OS.Windows) 1504 { 1505 if (target.omfobj) 1506 return toCppMangleDMC(s); 1507 else 1508 return toCppMangleMSVC(s); 1509 } 1510 else 1511 assert(0, "fix this"); 1512 } 1513 1514 /** 1515 * Get RTTI mangling of the given class declaration for C++ ABI. 1516 * Params: 1517 * cd = class with C++ linkage 1518 * Returns: 1519 * string mangling of C++ typeinfo 1520 */ 1521 extern (C++) const(char)* typeInfoMangle(ClassDeclaration cd) 1522 { 1523 import dmd.cppmangle : cppTypeInfoMangleItanium; 1524 import dmd.cppmanglewin : cppTypeInfoMangleDMC, cppTypeInfoMangleMSVC; 1525 1526 if (target.os & (Target.OS.linux | Target.OS.OSX | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD)) 1527 return cppTypeInfoMangleItanium(cd); 1528 if (target.os == Target.OS.Windows) 1529 { 1530 if (target.omfobj) 1531 return cppTypeInfoMangleDMC(cd); 1532 else 1533 return cppTypeInfoMangleMSVC(cd); 1534 } 1535 else 1536 assert(0, "fix this"); 1537 } 1538 1539 /** 1540 * Get mangle name of a this-adjusting thunk to the given function 1541 * declaration for C++ ABI. 1542 * Params: 1543 * fd = function with C++ linkage 1544 * offset = call offset to the vptr 1545 * Returns: 1546 * string mangling of C++ thunk, or null if unhandled 1547 */ 1548 extern (C++) const(char)* thunkMangle(FuncDeclaration fd, int offset) 1549 { 1550 return null; 1551 } 1552 1553 /** 1554 * Gets vendor-specific type mangling for C++ ABI. 1555 * Params: 1556 * t = type to inspect 1557 * Returns: 1558 * string if type is mangled specially on target 1559 * null if unhandled 1560 */ 1561 extern (C++) const(char)* typeMangle(Type t) 1562 { 1563 return null; 1564 } 1565 1566 /** 1567 * Get the type that will really be used for passing the given argument 1568 * to an `extern(C++)` function, or `null` if unhandled. 1569 * Params: 1570 * t = type to be passed. 1571 * Returns: 1572 * `Type` to use for type `t`. 1573 */ 1574 extern (C++) Type parameterType(Type t) 1575 { 1576 return null; 1577 } 1578 1579 /** 1580 * Checks whether type is a vendor-specific fundamental type. 1581 * Params: 1582 * t = type to inspect 1583 * isFundamental = where to store result 1584 * Returns: 1585 * true if isFundamental was set by function 1586 */ 1587 extern (C++) bool fundamentalType(const Type t, ref bool isFundamental) 1588 { 1589 return false; 1590 } 1591 1592 /** 1593 * Get the starting offset position for fields of an `extern(C++)` class 1594 * that is derived from the given base class. 1595 * Params: 1596 * baseClass = base class with C++ linkage 1597 * Returns: 1598 * starting offset to lay out derived class fields 1599 */ 1600 extern (C++) uint derivedClassOffset(ClassDeclaration baseClass) 1601 { 1602 // MSVC adds padding between base and derived fields if required. 1603 if (target.os == Target.OS.Windows) 1604 return (baseClass.structsize + baseClass.alignsize - 1) & ~(baseClass.alignsize - 1); 1605 else 1606 return baseClass.structsize; 1607 } 1608 } 1609 1610 //////////////////////////////////////////////////////////////////////////////// 1611 /** 1612 * Functions and variables specific to interface with extern(Objective-C) ABI. 1613 */ 1614 struct TargetObjC 1615 { 1616 bool supported; /// set if compiler can interface with Objective-C 1617 1618 extern (D) void initialize(ref const Param params, ref const Target target) 1619 { 1620 if (target.os == Target.OS.OSX && target.is64bit) 1621 supported = true; 1622 } 1623 } 1624 1625 //////////////////////////////////////////////////////////////////////////////// 1626 extern (C++) __gshared Target target;