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