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