1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes.d, _argtypes.d)
9  * Documentation:  https://dlang.org/phobos/dmd_argtypes.html
10  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes.d
11  */
12 
13 module dmd.argtypes;
14 
15 import core.stdc.stdio;
16 import core.checkedint;
17 
18 import dmd.declaration;
19 import dmd.globals;
20 import dmd.mtype;
21 import dmd.visitor;
22 
23 /****************************************************
24  * This breaks a type down into 'simpler' types that can be passed to a function
25  * in registers, and returned in registers.
26  * It's highly platform dependent.
27  * Params:
28  *      t = type to break down
29  * Returns:
30  *      tuple of types, each element can be passed in a register.
31  *      A tuple of zero length means the type cannot be passed/returned in registers.
32  *      null indicates a `void`.
33  * References:
34  *  For 64 bit code, follows Itanium C++ ABI 1.86 Chapter 3
35  *  http://refspecs.linux-foundation.org/cxxabi-1.86.html#calls
36  */
37 TypeTuple toArgTypes(Type t)
38 {
39     extern (C++) final class ToArgTypes : Visitor
40     {
41         alias visit = Visitor.visit;
42     public:
43         TypeTuple result;
44 
45         /*****
46          * Pass type in memory (i.e. on the stack), a tuple of one type, or a tuple of 2 types
47          */
48         void memory()
49         {
50             //printf("\ttoArgTypes() %s => [ ]\n", t.toChars());
51             result = new TypeTuple(); // pass on the stack
52         }
53 
54         ///
55         void oneType(Type t)
56         {
57             result = new TypeTuple(t);
58         }
59 
60         ///
61         void twoTypes(Type t1, Type t2)
62         {
63             result = new TypeTuple(t1, t2);
64         }
65 
66 
67         override void visit(Type)
68         {
69             // not valid for a parameter
70         }
71 
72         override void visit(TypeError)
73         {
74             result = new TypeTuple(Type.terror);
75         }
76 
77         override void visit(TypeBasic t)
78         {
79             Type t1 = null;
80             Type t2 = null;
81             switch (t.ty)
82             {
83             case Tvoid:
84                 return;
85             case Tbool:
86             case Tint8:
87             case Tuns8:
88             case Tint16:
89             case Tuns16:
90             case Tint32:
91             case Tuns32:
92             case Tfloat32:
93             case Tint64:
94             case Tuns64:
95             case Tint128:
96             case Tuns128:
97             case Tfloat64:
98             case Tfloat80:
99                 t1 = t;
100                 break;
101             case Timaginary32:
102                 t1 = Type.tfloat32;
103                 break;
104             case Timaginary64:
105                 t1 = Type.tfloat64;
106                 break;
107             case Timaginary80:
108                 t1 = Type.tfloat80;
109                 break;
110             case Tcomplex32:
111                 if (global.params.is64bit)
112                     t1 = Type.tfloat64;
113                 else
114                 {
115                     t1 = Type.tfloat64;
116                     t2 = Type.tfloat64;
117                 }
118                 break;
119             case Tcomplex64:
120                 t1 = Type.tfloat64;
121                 t2 = Type.tfloat64;
122                 break;
123             case Tcomplex80:
124                 t1 = Type.tfloat80;
125                 t2 = Type.tfloat80;
126                 break;
127             case Tchar:
128                 t1 = Type.tuns8;
129                 break;
130             case Twchar:
131                 t1 = Type.tuns16;
132                 break;
133             case Tdchar:
134                 t1 = Type.tuns32;
135                 break;
136             default:
137                 assert(0);
138             }
139             if (t1)
140             {
141                 if (t2)
142                     return twoTypes(t1, t2);
143                 else
144                     return oneType(t1);
145             }
146             else
147                 return memory();
148         }
149 
150         override void visit(TypeVector t)
151         {
152             return oneType(t);
153         }
154 
155         override void visit(TypeAArray)
156         {
157             return oneType(Type.tvoidptr);
158         }
159 
160         override void visit(TypePointer)
161         {
162             return oneType(Type.tvoidptr);
163         }
164 
165         /*************************************
166          * Convert a floating point type into the equivalent integral type.
167          */
168         static Type mergeFloatToInt(Type t)
169         {
170             switch (t.ty)
171             {
172             case Tfloat32:
173             case Timaginary32:
174                 t = Type.tint32;
175                 break;
176             case Tfloat64:
177             case Timaginary64:
178             case Tcomplex32:
179                 t = Type.tint64;
180                 break;
181             default:
182                 debug
183                 {
184                     printf("mergeFloatToInt() %s\n", t.toChars());
185                 }
186                 assert(0);
187             }
188             return t;
189         }
190 
191         /*************************************
192          * This merges two types into an 8byte type.
193          * Params:
194          *      t1 = first type (can be null)
195          *      t2 = second type (can be null)
196          *      offset2 = offset of t2 from start of t1
197          * Returns:
198          *      type that encompasses both t1 and t2, null if cannot be done
199          */
200         static Type argtypemerge(Type t1, Type t2, uint offset2)
201         {
202             //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1.toChars() : "", t2 ? t2.toChars() : "", offset2);
203             if (!t1)
204             {
205                 assert(!t2 || offset2 == 0);
206                 return t2;
207             }
208             if (!t2)
209                 return t1;
210             const sz1 = t1.size(Loc.initial);
211             const sz2 = t2.size(Loc.initial);
212             assert(sz1 != SIZE_INVALID && sz2 != SIZE_INVALID);
213             if (t1.ty != t2.ty && (t1.ty == Tfloat80 || t2.ty == Tfloat80))
214                 return null;
215             // [float,float] => [cfloat]
216             if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4)
217                 return Type.tfloat64;
218             // Merging floating and non-floating types produces the non-floating type
219             if (t1.isfloating())
220             {
221                 if (!t2.isfloating())
222                     t1 = mergeFloatToInt(t1);
223             }
224             else if (t2.isfloating())
225                 t2 = mergeFloatToInt(t2);
226             Type t;
227             // Pick type with larger size
228             if (sz1 < sz2)
229                 t = t2;
230             else
231                 t = t1;
232             // If t2 does not lie within t1, need to increase the size of t to enclose both
233             bool overflow;
234             const offset3 = addu(offset2, sz2, overflow);
235             assert(!overflow);
236             if (offset2 && sz1 < offset3)
237             {
238                 switch (offset3)
239                 {
240                 case 2:
241                     t = Type.tint16;
242                     break;
243                 case 3:
244                 case 4:
245                     t = Type.tint32;
246                     break;
247                 default:
248                     t = Type.tint64;
249                     break;
250                 }
251             }
252             return t;
253         }
254 
255         override void visit(TypeDArray)
256         {
257             /* Should be done as if it were:
258              * struct S { size_t length; void* ptr; }
259              */
260             if (global.params.is64bit && !global.params.isLP64)
261             {
262                 // For AMD64 ILP32 ABI, D arrays fit into a single integer register.
263                 const offset = cast(uint)Type.tsize_t.size(Loc.initial);
264                 Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset);
265                 if (t)
266                 {
267                     return oneType(t);
268                 }
269             }
270             return twoTypes(Type.tsize_t, Type.tvoidptr);
271         }
272 
273         override void visit(TypeDelegate)
274         {
275             /* Should be done as if it were:
276              * struct S { void* funcptr; void* ptr; }
277              */
278             if (global.params.is64bit && !global.params.isLP64)
279             {
280                 // For AMD64 ILP32 ABI, delegates fit into a single integer register.
281                 const offset = cast(uint)Type.tsize_t.size(Loc.initial);
282                 Type t = argtypemerge(Type.tvoidptr, Type.tvoidptr, offset);
283                 if (t)
284                 {
285                     return oneType(t);
286                 }
287             }
288             return twoTypes(Type.tvoidptr, Type.tvoidptr);
289         }
290 
291         override void visit(TypeSArray t)
292         {
293             const sz = t.size(Loc.initial);
294             if (sz > 16)
295                 return memory();
296 
297             const dim = t.dim.toInteger();
298             Type tn = t.next;
299             const tnsize = tn.size();
300             const tnalignsize = tn.alignsize();
301 
302             /*****
303              * Get the nth element of this array.
304              * Params:
305              *   n = element number, from 0..dim
306              *   offset = set to offset of the element from the start of the array
307              *   alignsize = set to the aligned size of the element
308              * Returns:
309              *   type of the element
310              */
311             extern (D) Type getNthElement(size_t n, out uint offset, out uint alignsize)
312             {
313                 offset = cast(uint)(n * tnsize);
314                 alignsize = tnalignsize;
315                 return tn;
316             }
317 
318             aggregate(sz, cast(size_t)dim, &getNthElement);
319         }
320 
321         override void visit(TypeStruct t)
322         {
323             //printf("TypeStruct.toArgTypes() %s\n", t.toChars());
324 
325             if (!t.sym.isPOD())
326                 return memory();
327 
328             /*****
329              * Get the nth field of this struct.
330              * Params:
331              *   n = field number, from 0..nfields
332              *   offset = set to offset of the field from the start of the type
333              *   alignsize = set to the aligned size of the field
334              * Returns:
335              *   type of the field
336              */
337             extern (D) Type getNthField(size_t n, out uint offset, out uint alignsize)
338             {
339                 auto field = t.sym.fields[n];
340                 offset = field.offset;
341                 alignsize = field.type.alignsize();
342                 return field.type;
343             }
344 
345             aggregate(t.size(Loc.initial), t.sym.fields.dim, &getNthField);
346         }
347 
348         /*******************
349          * Handle aggregates (struct, union, and static array) and set `result`
350          * Params:
351          *      sz = total size of aggregate
352          *      nfields = number of fields in the aggregate (dimension for static arrays)
353          *      getFieldInfo = get information about the nth field in the aggregate
354          */
355         extern (D) void aggregate(d_uns64 sz, size_t nfields, Type delegate(size_t, out uint, out uint) getFieldInfo)
356         {
357             if (nfields == 0)
358                 return memory();
359 
360             if (global.params.is64bit)
361             {
362                 if (sz == 0 || sz > 16)
363                     return memory();
364 
365                 Type t1 = null;
366                 Type t2 = null;
367 
368                 foreach (n; 0 .. nfields)
369                 {
370                     uint foffset;
371                     uint falignsize;
372                     Type ftype = getFieldInfo(n, foffset, falignsize);
373 
374                     //printf("  [%u] ftype = %s\n", n, ftype.toChars());
375                     TypeTuple tup = toArgTypes(ftype);
376                     if (!tup)
377                         return memory();
378                     const dim = tup.arguments.dim;
379                     Type ft1 = null;
380                     Type ft2 = null;
381                     switch (dim)
382                     {
383                     case 2:
384                         ft1 = (*tup.arguments)[0].type;
385                         ft2 = (*tup.arguments)[1].type;
386                         break;
387                     case 1:
388                         if (foffset < 8)
389                             ft1 = (*tup.arguments)[0].type;
390                         else
391                             ft2 = (*tup.arguments)[0].type;
392                         break;
393                     default:
394                         return memory();
395                     }
396                     if (foffset & 7)
397                     {
398                         // Misaligned fields goto Lmemory
399                         if (foffset & (falignsize - 1))
400                             return memory();
401 
402                         // Fields that overlap the 8byte boundary goto memory
403                         const fieldsz = ftype.size(Loc.initial);
404                         bool overflow;
405                         const nextOffset = addu(foffset, fieldsz, overflow);
406                         assert(!overflow);
407                         if (foffset < 8 && nextOffset > 8)
408                             return memory();
409                     }
410                     // First field in 8byte must be at start of 8byte
411                     assert(t1 || foffset == 0);
412                     //printf("ft1 = %s\n", ft1 ? ft1.toChars() : "null");
413                     //printf("ft2 = %s\n", ft2 ? ft2.toChars() : "null");
414                     if (ft1)
415                     {
416                         t1 = argtypemerge(t1, ft1, foffset);
417                         if (!t1)
418                             return memory();
419                     }
420                     if (ft2)
421                     {
422                         const off2 = ft1 ? 8 : foffset;
423                         if (!t2 && off2 != 8)
424                             return memory();
425                         assert(t2 || off2 == 8);
426                         t2 = argtypemerge(t2, ft2, off2 - 8);
427                         if (!t2)
428                             return memory();
429                     }
430                 }
431                 if (t2)
432                 {
433                     if (t1.isfloating() && t2.isfloating())
434                     {
435                         if ((t1.ty == Tfloat32 || t1.ty == Tfloat64) && (t2.ty == Tfloat32 || t2.ty == Tfloat64))
436                         {
437                         }
438                         else
439                             return memory();
440                     }
441                     else if (t1.isfloating() || t2.isfloating())
442                         return memory();
443                     return twoTypes(t1, t2);
444                 }
445 
446                 //printf("\ttoArgTypes() %s => [%s,%s]\n", t.toChars(), t1 ? t1.toChars() : "", t2 ? t2.toChars() : "");
447                 if (t1)
448                     return oneType(t1);
449                 else
450                     return memory();
451             }
452             else
453             {
454                 Type t1 = null;
455                 switch (cast(uint)sz)
456                 {
457                 case 1:
458                     t1 = Type.tint8;
459                     break;
460                 case 2:
461                     t1 = Type.tint16;
462                     break;
463                 case 4:
464                     t1 = Type.tint32;
465                     break;
466                 case 8:
467                     t1 = Type.tint64;
468                     break;
469                 case 16:
470                     t1 = null; // could be a TypeVector
471                     break;
472                 default:
473                     return memory();
474                 }
475                 if (global.params.isFreeBSD && nfields == 1 &&
476                     (sz == 4 || sz == 8))
477                 {
478                     /* FreeBSD changed their 32 bit ABI at some point before 10.3 for the following:
479                      *  struct { float f;  } => arg1type is float
480                      *  struct { double d; } => arg1type is double
481                      * Cannot find any documentation on it.
482                      */
483 
484                     uint foffset;
485                     uint falignsize;
486                     Type ftype = getFieldInfo(0, foffset, falignsize);
487                     TypeTuple tup = toArgTypes(ftype);
488                     if (tup && tup.arguments.dim == 1)
489                     {
490                         Type ft1 = (*tup.arguments)[0].type;
491                         if (ft1.ty == Tfloat32 || ft1.ty == Tfloat64)
492                             return oneType(ft1);
493                     }
494                 }
495 
496                 if (t1)
497                     return oneType(t1);
498                 else
499                     return memory();
500             }
501         }
502 
503         override void visit(TypeEnum t)
504         {
505             t.toBasetype().accept(this);
506         }
507 
508         override void visit(TypeClass)
509         {
510             result = new TypeTuple(Type.tvoidptr);
511         }
512     }
513 
514     scope ToArgTypes v = new ToArgTypes();
515     t.accept(v);
516     return v.result;
517 }
Suggestion Box / Bug Report