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