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