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