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