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