1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, 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:      $(DMDSRC _argtypes.d)
9  */
10 
11 module ddmd.argtypes;
12 
13 import core.stdc.stdio;
14 import ddmd.declaration;
15 import ddmd.globals;
16 import ddmd.mtype;
17 import ddmd.visitor;
18 
19 /****************************************************
20  * This breaks a type down into 'simpler' types that can be passed to a function
21  * in registers, and returned in registers.
22  * It's highly platform dependent.
23  * Params:
24  *      t = type to break down
25  * Returns:
26  *      tuple of types, each element can be passed in a register.
27  *      A tuple of zero length means the type cannot be passed/returned in registers.
28  */
29 extern (C++) TypeTuple toArgTypes(Type t)
30 {
31     extern (C++) final class ToArgTypes : Visitor
32     {
33         alias visit = super.visit;
34     public:
35         TypeTuple result;
36 
37         override void visit(Type)
38         {
39             // not valid for a parameter
40         }
41 
42         override void visit(TypeError)
43         {
44             result = new TypeTuple(Type.terror);
45         }
46 
47         override void visit(TypeBasic t)
48         {
49             Type t1 = null;
50             Type t2 = null;
51             switch (t.ty)
52             {
53             case Tvoid:
54                 return;
55             case Tbool:
56             case Tint8:
57             case Tuns8:
58             case Tint16:
59             case Tuns16:
60             case Tint32:
61             case Tuns32:
62             case Tfloat32:
63             case Tint64:
64             case Tuns64:
65             case Tint128:
66             case Tuns128:
67             case Tfloat64:
68             case Tfloat80:
69                 t1 = t;
70                 break;
71             case Timaginary32:
72                 t1 = Type.tfloat32;
73                 break;
74             case Timaginary64:
75                 t1 = Type.tfloat64;
76                 break;
77             case Timaginary80:
78                 t1 = Type.tfloat80;
79                 break;
80             case Tcomplex32:
81                 if (global.params.is64bit)
82                     t1 = Type.tfloat64;
83                 else
84                 {
85                     t1 = Type.tfloat64;
86                     t2 = Type.tfloat64;
87                 }
88                 break;
89             case Tcomplex64:
90                 t1 = Type.tfloat64;
91                 t2 = Type.tfloat64;
92                 break;
93             case Tcomplex80:
94                 t1 = Type.tfloat80;
95                 t2 = Type.tfloat80;
96                 break;
97             case Tchar:
98                 t1 = Type.tuns8;
99                 break;
100             case Twchar:
101                 t1 = Type.tuns16;
102                 break;
103             case Tdchar:
104                 t1 = Type.tuns32;
105                 break;
106             default:
107                 assert(0);
108             }
109             if (t1)
110             {
111                 if (t2)
112                     result = new TypeTuple(t1, t2);
113                 else
114                     result = new TypeTuple(t1);
115             }
116             else
117                 result = new TypeTuple();
118         }
119 
120         override void visit(TypeVector t)
121         {
122             result = new TypeTuple(t);
123         }
124 
125         override void visit(TypeSArray t)
126         {
127             if (t.dim)
128             {
129                 /* Should really be done as if it were a struct with dim members
130                  * of the array's elements.
131                  * I.e. int[2] should be done like struct S { int a; int b; }
132                  */
133                 dinteger_t sz = t.dim.toInteger();
134                 // T[1] should be passed like T
135                 if (sz == 1)
136                 {
137                     t.next.accept(this);
138                     return;
139                 }
140             }
141             result = new TypeTuple(); // pass on the stack for efficiency
142         }
143 
144         override void visit(TypeAArray)
145         {
146             result = new TypeTuple(Type.tvoidptr);
147         }
148 
149         override void visit(TypePointer)
150         {
151             result = new TypeTuple(Type.tvoidptr);
152         }
153 
154         /*************************************
155          * Convert a floating point type into the equivalent integral type.
156          */
157         static Type mergeFloatToInt(Type t)
158         {
159             switch (t.ty)
160             {
161             case Tfloat32:
162             case Timaginary32:
163                 t = Type.tint32;
164                 break;
165             case Tfloat64:
166             case Timaginary64:
167             case Tcomplex32:
168                 t = Type.tint64;
169                 break;
170             default:
171                 debug
172                 {
173                     printf("mergeFloatToInt() %s\n", t.toChars());
174                 }
175                 assert(0);
176             }
177             return t;
178         }
179 
180         /*************************************
181          * This merges two types into an 8byte type.
182          * Params:
183          *      t1 = first type (can be null)
184          *      t2 = second type (can be null)
185          *      offset2 = offset of t2 from start of t1
186          * Returns:
187          *      type that encompasses both t1 and t2, null if cannot be done
188          */
189         static Type argtypemerge(Type t1, Type t2, uint offset2)
190         {
191             //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1.toChars() : "", t2 ? t2.toChars() : "", offset2);
192             if (!t1)
193             {
194                 assert(!t2 || offset2 == 0);
195                 return t2;
196             }
197             if (!t2)
198                 return t1;
199             const sz1 = t1.size(Loc());
200             const sz2 = t2.size(Loc());
201             assert(sz1 != SIZE_INVALID && sz2 != SIZE_INVALID);
202             if (t1.ty != t2.ty && (t1.ty == Tfloat80 || t2.ty == Tfloat80))
203                 return null;
204             // [float,float] => [cfloat]
205             if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4)
206                 return Type.tfloat64;
207             // Merging floating and non-floating types produces the non-floating type
208             if (t1.isfloating())
209             {
210                 if (!t2.isfloating())
211                     t1 = mergeFloatToInt(t1);
212             }
213             else if (t2.isfloating())
214                 t2 = mergeFloatToInt(t2);
215             Type t;
216             // Pick type with larger size
217             if (sz1 < sz2)
218                 t = t2;
219             else
220                 t = t1;
221             // If t2 does not lie within t1, need to increase the size of t to enclose both
222             assert(sz2 < sz2.max - offset2.max);
223             if (offset2 && sz1 < offset2 + sz2)
224             {
225                 switch (offset2 + sz2)
226                 {
227                 case 2:
228                     t = Type.tint16;
229                     break;
230                 case 3:
231                 case 4:
232                     t = Type.tint32;
233                     break;
234                 default:
235                     t = Type.tint64;
236                     break;
237                 }
238             }
239             return t;
240         }
241 
242         override void visit(TypeDArray)
243         {
244             /* Should be done as if it were:
245              * struct S { size_t length; void* ptr; }
246              */
247             if (global.params.is64bit && !global.params.isLP64)
248             {
249                 // For AMD64 ILP32 ABI, D arrays fit into a single integer register.
250                 uint offset = cast(uint)Type.tsize_t.size(Loc());
251                 Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset);
252                 if (t)
253                 {
254                     result = new TypeTuple(t);
255                     return;
256                 }
257             }
258             result = new TypeTuple(Type.tsize_t, Type.tvoidptr);
259         }
260 
261         override void visit(TypeDelegate)
262         {
263             /* Should be done as if it were:
264              * struct S { size_t length; void* ptr; }
265              */
266             if (global.params.is64bit && !global.params.isLP64)
267             {
268                 // For AMD64 ILP32 ABI, delegates fit into a single integer register.
269                 uint offset = cast(uint)Type.tsize_t.size(Loc());
270                 Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset);
271                 if (t)
272                 {
273                     result = new TypeTuple(t);
274                     return;
275                 }
276             }
277             result = new TypeTuple(Type.tvoidptr, Type.tvoidptr);
278         }
279 
280         override void visit(TypeStruct t)
281         {
282             //printf("TypeStruct.toArgTypes() %s\n", t.toChars());
283             if (!t.sym.isPOD() || t.sym.fields.dim == 0)
284             {
285             Lmemory:
286                 //printf("\ttoArgTypes() %s => [ ]\n", t->toChars());
287                 result = new TypeTuple(); // pass on the stack
288                 return;
289             }
290             Type t1 = null;
291             Type t2 = null;
292             const sz = t.size(Loc());
293             assert(sz < 0xFFFFFFFF);
294             switch (cast(uint)sz)
295             {
296             case 1:
297                 t1 = Type.tint8;
298                 break;
299             case 2:
300                 t1 = Type.tint16;
301                 break;
302             case 3:
303                 if (!global.params.is64bit)
304                     goto Lmemory;
305                 goto case;
306             case 4:
307                 t1 = Type.tint32;
308                 break;
309             case 5:
310             case 6:
311             case 7:
312                 if (!global.params.is64bit)
313                     goto Lmemory;
314                 goto case;
315             case 8:
316                 t1 = Type.tint64;
317                 break;
318             case 16:
319                 t1 = null; // could be a TypeVector
320                 break;
321             case 9:
322             case 10:
323             case 11:
324             case 12:
325             case 13:
326             case 14:
327             case 15:
328                 if (!global.params.is64bit)
329                     goto Lmemory;
330                 t1 = null;
331                 break;
332             default:
333                 goto Lmemory;
334             }
335             if (global.params.is64bit && t.sym.fields.dim)
336             {
337                 version (all)
338                 {
339                     t1 = null;
340                     for (size_t i = 0; i < t.sym.fields.dim; i++)
341                     {
342                         VarDeclaration f = t.sym.fields[i];
343                         //printf("  [%d] %s f.type = %s\n", cast(int)i, f.toChars(), f.type.toChars());
344                         TypeTuple tup = toArgTypes(f.type);
345                         if (!tup)
346                             goto Lmemory;
347                         size_t dim = tup.arguments.dim;
348                         Type ft1 = null;
349                         Type ft2 = null;
350                         switch (dim)
351                         {
352                         case 2:
353                             ft1 = (*tup.arguments)[0].type;
354                             ft2 = (*tup.arguments)[1].type;
355                             break;
356                         case 1:
357                             if (f.offset < 8)
358                                 ft1 = (*tup.arguments)[0].type;
359                             else
360                                 ft2 = (*tup.arguments)[0].type;
361                             break;
362                         default:
363                             goto Lmemory;
364                         }
365                         if (f.offset & 7)
366                         {
367                             // Misaligned fields goto Lmemory
368                             uint alignsz = f.type.alignsize();
369                             if (f.offset & (alignsz - 1))
370                                 goto Lmemory;
371                             // Fields that overlap the 8byte boundary goto Lmemory
372                             const fieldsz = f.type.size(Loc());
373                             assert(fieldsz != SIZE_INVALID && fieldsz < fieldsz.max - f.offset.max);
374                             if (f.offset < 8 && (f.offset + fieldsz) > 8)
375                                 goto Lmemory;
376                         }
377                         // First field in 8byte must be at start of 8byte
378                         assert(t1 || f.offset == 0);
379                         //printf("ft1 = %s\n", ft1 ? ft1.toChars() : "null");
380                         //printf("ft2 = %s\n", ft2 ? ft2.toChars() : "null");
381                         if (ft1)
382                         {
383                             t1 = argtypemerge(t1, ft1, f.offset);
384                             if (!t1)
385                                 goto Lmemory;
386                         }
387                         if (ft2)
388                         {
389                             uint off2 = f.offset;
390                             if (ft1)
391                                 off2 = 8;
392                             if (!t2 && off2 != 8)
393                                 goto Lmemory;
394                             assert(t2 || off2 == 8);
395                             t2 = argtypemerge(t2, ft2, off2 - 8);
396                             if (!t2)
397                                 goto Lmemory;
398                         }
399                     }
400                     if (t2)
401                     {
402                         if (t1.isfloating() && t2.isfloating())
403                         {
404                             if ((t1.ty == Tfloat32 || t1.ty == Tfloat64) && (t2.ty == Tfloat32 || t2.ty == Tfloat64))
405                             {
406                             }
407                             else
408                                 goto Lmemory;
409                         }
410                         else if (t1.isfloating())
411                             goto Lmemory;
412                         else if (t2.isfloating())
413                             goto Lmemory;
414                         else
415                         {
416                         }
417                     }
418                 }
419                 else
420                 {
421                     if (t.sym.fields.dim == 1)
422                     {
423                         VarDeclaration f = t.sym.fields[0];
424                         //printf("f->type = %s\n", f->type->toChars());
425                         TypeTuple tup = toArgTypes(f.type);
426                         if (tup)
427                         {
428                             size_t dim = tup.arguments.dim;
429                             if (dim == 1)
430                                 t1 = (*tup.arguments)[0].type;
431                         }
432                     }
433                 }
434             }
435             //printf("\ttoArgTypes() %s => [%s,%s]\n", t->toChars(), t1 ? t1->toChars() : "", t2 ? t2->toChars() : "");
436             if (t1)
437             {
438                 //if (t1) printf("test1: %s => %s\n", toChars(), t1->toChars());
439                 if (t2)
440                     result = new TypeTuple(t1, t2);
441                 else
442                     result = new TypeTuple(t1);
443             }
444             else
445                 goto Lmemory;
446         }
447 
448         override void visit(TypeEnum t)
449         {
450             t.toBasetype().accept(this);
451         }
452 
453         override void visit(TypeClass)
454         {
455             result = new TypeTuple(Type.tvoidptr);
456         }
457     }
458 
459     scope ToArgTypes v = new ToArgTypes();
460     t.accept(v);
461     return v.result;
462 }