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