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 = &params;
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;