1 /**
2  * Break down a D type into basic (register) types for the 32-bit x86 ABI.
3  *
4  * Copyright:   Copyright (C) 1999-2021 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_x86.d, _argtypes_x86.d)
8  * Documentation:  https://dlang.org/phobos/dmd_argtypes_x86.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_x86.d
10  */
11 
12 module dmd.argtypes_x86;
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 /****************************************************
23  * This breaks a type down into 'simpler' types that can be passed to a function
24  * in registers, and returned in registers.
25  * This is the implementation for the 32-bit x86 ABI.
26  * Params:
27  *      t = type to break down
28  * Returns:
29  *      tuple of types, each element can be passed in a register.
30  *      A tuple of zero length means the type cannot be passed/returned in registers.
31  *      null indicates a `void`.
32  */
33 extern (C++) TypeTuple toArgTypes_x86(Type t)
34 {
35     extern (C++) final class ToArgTypes : Visitor
36     {
37         alias visit = Visitor.visit;
38     public:
39         TypeTuple result;
40 
41         /*****
42          * Pass type in memory (i.e. on the stack), a tuple of one type, or a tuple of 2 types
43          */
44         void memory()
45         {
46             //printf("\ttoArgTypes() %s => [ ]\n", t.toChars());
47             result = TypeTuple.empty; // pass on the stack
48         }
49 
50         ///
51         void oneType(Type t)
52         {
53             result = new TypeTuple(t);
54         }
55 
56         ///
57         void twoTypes(Type t1, Type t2)
58         {
59             result = new TypeTuple(t1, t2);
60         }
61 
62 
63         override void visit(Type)
64         {
65             // not valid for a parameter
66         }
67 
68         override void visit(TypeError)
69         {
70             result = new TypeTuple(Type.terror);
71         }
72 
73         override void visit(TypeBasic t)
74         {
75             Type t1 = null;
76             Type t2 = null;
77             switch (t.ty)
78             {
79             case Tvoid:
80                 return;
81             case Tbool:
82             case Tint8:
83             case Tuns8:
84             case Tint16:
85             case Tuns16:
86             case Tint32:
87             case Tuns32:
88             case Tfloat32:
89             case Tint64:
90             case Tuns64:
91             case Tint128:
92             case Tuns128:
93             case Tfloat64:
94             case Tfloat80:
95                 t1 = t;
96                 break;
97             case Timaginary32:
98                 t1 = Type.tfloat32;
99                 break;
100             case Timaginary64:
101                 t1 = Type.tfloat64;
102                 break;
103             case Timaginary80:
104                 t1 = Type.tfloat80;
105                 break;
106             case Tcomplex32:
107                 t1 = Type.tfloat64;
108                 t2 = Type.tfloat64;
109                 break;
110             case Tcomplex64:
111                 t1 = Type.tfloat64;
112                 t2 = Type.tfloat64;
113                 break;
114             case Tcomplex80:
115                 t1 = Type.tfloat80;
116                 t2 = Type.tfloat80;
117                 break;
118             case Tchar:
119                 t1 = Type.tuns8;
120                 break;
121             case Twchar:
122                 t1 = Type.tuns16;
123                 break;
124             case Tdchar:
125                 t1 = Type.tuns32;
126                 break;
127             default:
128                 assert(0);
129             }
130             if (t1)
131             {
132                 if (t2)
133                     return twoTypes(t1, t2);
134                 else
135                     return oneType(t1);
136             }
137             else
138                 return memory();
139         }
140 
141         override void visit(TypeVector t)
142         {
143             return oneType(t);
144         }
145 
146         override void visit(TypeAArray)
147         {
148             return oneType(Type.tvoidptr);
149         }
150 
151         override void visit(TypePointer)
152         {
153             return oneType(Type.tvoidptr);
154         }
155 
156         /*************************************
157          * Convert a floating point type into the equivalent integral type.
158          */
159         static Type mergeFloatToInt(Type t)
160         {
161             switch (t.ty)
162             {
163             case Tfloat32:
164             case Timaginary32:
165                 t = Type.tint32;
166                 break;
167             case Tfloat64:
168             case Timaginary64:
169             case Tcomplex32:
170                 t = Type.tint64;
171                 break;
172             default:
173                 debug
174                 {
175                     printf("mergeFloatToInt() %s\n", t.toChars());
176                 }
177                 assert(0);
178             }
179             return t;
180         }
181 
182         /*************************************
183          * This merges two types into an 8byte type.
184          * Params:
185          *      t1 = first type (can be null)
186          *      t2 = second type (can be null)
187          *      offset2 = offset of t2 from start of t1
188          * Returns:
189          *      type that encompasses both t1 and t2, null if cannot be done
190          */
191         static Type argtypemerge(Type t1, Type t2, uint offset2)
192         {
193             //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1.toChars() : "", t2 ? t2.toChars() : "", offset2);
194             if (!t1)
195             {
196                 assert(!t2 || offset2 == 0);
197                 return t2;
198             }
199             if (!t2)
200                 return t1;
201             const sz1 = t1.size(Loc.initial);
202             const sz2 = t2.size(Loc.initial);
203             assert(sz1 != SIZE_INVALID && sz2 != SIZE_INVALID);
204             if (t1.ty != t2.ty && (t1.ty == Tfloat80 || t2.ty == Tfloat80))
205                 return null;
206             // [float,float] => [cfloat]
207             if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4)
208                 return Type.tfloat64;
209             // Merging floating and non-floating types produces the non-floating type
210             if (t1.isfloating())
211             {
212                 if (!t2.isfloating())
213                     t1 = mergeFloatToInt(t1);
214             }
215             else if (t2.isfloating())
216                 t2 = mergeFloatToInt(t2);
217             Type t;
218             // Pick type with larger size
219             if (sz1 < sz2)
220                 t = t2;
221             else
222                 t = t1;
223             // If t2 does not lie within t1, need to increase the size of t to enclose both
224             bool overflow;
225             const offset3 = addu(offset2, sz2, overflow);
226             assert(!overflow);
227             if (offset2 && sz1 < offset3)
228             {
229                 switch (offset3)
230                 {
231                 case 2:
232                     t = Type.tint16;
233                     break;
234                 case 3:
235                 case 4:
236                     t = Type.tint32;
237                     break;
238                 default:
239                     t = Type.tint64;
240                     break;
241                 }
242             }
243             return t;
244         }
245 
246         override void visit(TypeDArray)
247         {
248             /* Should be done as if it were:
249              * struct S { size_t length; void* ptr; }
250              */
251             return twoTypes(Type.tsize_t, Type.tvoidptr);
252         }
253 
254         override void visit(TypeDelegate)
255         {
256             /* Should be done as if it were:
257              * struct S { void* funcptr; void* ptr; }
258              */
259             return twoTypes(Type.tvoidptr, Type.tvoidptr);
260         }
261 
262         override void visit(TypeSArray t)
263         {
264             const sz = t.size(Loc.initial);
265             if (sz > 16)
266                 return memory();
267 
268             const dim = t.dim.toInteger();
269             Type tn = t.next;
270             const tnsize = tn.size();
271             const tnalignsize = tn.alignsize();
272 
273             /*****
274              * Get the nth element of this array.
275              * Params:
276              *   n = element number, from 0..dim
277              *   offset = set to offset of the element from the start of the array
278              *   alignsize = set to the aligned size of the element
279              * Returns:
280              *   type of the element
281              */
282             extern (D) Type getNthElement(size_t n, out uint offset, out uint alignsize)
283             {
284                 offset = cast(uint)(n * tnsize);
285                 alignsize = tnalignsize;
286                 return tn;
287             }
288 
289             aggregate(sz, cast(size_t)dim, &getNthElement);
290         }
291 
292         override void visit(TypeStruct t)
293         {
294             //printf("TypeStruct.toArgTypes() %s\n", t.toChars());
295 
296             if (!t.sym.isPOD())
297                 return memory();
298 
299             /*****
300              * Get the nth field of this struct.
301              * Params:
302              *   n = field number, from 0..nfields
303              *   offset = set to offset of the field from the start of the type
304              *   alignsize = set to the aligned size of the field
305              * Returns:
306              *   type of the field
307              */
308             extern (D) Type getNthField(size_t n, out uint offset, out uint alignsize)
309             {
310                 auto field = t.sym.fields[n];
311                 offset = field.offset;
312                 alignsize = field.type.alignsize();
313                 return field.type;
314             }
315 
316             aggregate(t.size(Loc.initial), t.sym.fields.dim, &getNthField);
317         }
318 
319         /*******************
320          * Handle aggregates (struct, union, and static array) and set `result`
321          * Params:
322          *      sz = total size of aggregate
323          *      nfields = number of fields in the aggregate (dimension for static arrays)
324          *      getFieldInfo = get information about the nth field in the aggregate
325          */
326         extern (D) void aggregate(d_uns64 sz, size_t nfields, Type delegate(size_t, out uint, out uint) getFieldInfo)
327         {
328             if (nfields == 0)
329                 return memory();
330 
331             Type t1 = null;
332             switch (cast(uint)sz)
333             {
334             case 1:
335                 t1 = Type.tint8;
336                 break;
337             case 2:
338                 t1 = Type.tint16;
339                 break;
340             case 4:
341                 t1 = Type.tint32;
342                 break;
343             case 8:
344                 t1 = Type.tint64;
345                 break;
346             case 16:
347                 t1 = null; // could be a TypeVector
348                 break;
349             default:
350                 return memory();
351             }
352             if (global.params.targetOS == TargetOS.FreeBSD && nfields == 1 &&
353                 (sz == 4 || sz == 8))
354             {
355                 /* FreeBSD changed their 32 bit ABI at some point before 10.3 for the following:
356                  *  struct { float f;  } => arg1type is float
357                  *  struct { double d; } => arg1type is double
358                  * Cannot find any documentation on it.
359                  */
360 
361                 uint foffset;
362                 uint falignsize;
363                 Type ftype = getFieldInfo(0, foffset, falignsize);
364                 TypeTuple tup = toArgTypes_x86(ftype);
365                 if (tup && tup.arguments.dim == 1)
366                 {
367                     Type ft1 = (*tup.arguments)[0].type;
368                     if (ft1.ty == Tfloat32 || ft1.ty == Tfloat64)
369                         return oneType(ft1);
370                 }
371             }
372 
373             if (t1)
374                 return oneType(t1);
375             else
376                 return memory();
377         }
378 
379         override void visit(TypeEnum t)
380         {
381             t.toBasetype().accept(this);
382         }
383 
384         override void visit(TypeClass)
385         {
386             result = new TypeTuple(Type.tvoidptr);
387         }
388     }
389 
390     scope ToArgTypes v = new ToArgTypes();
391     t.accept(v);
392     return v.result;
393 }