1 /**
2  * D header file for C99.
3  *
4  * Copyright: Public Domain
5  * License:   Public Domain
6  * Authors:   Hauke Duden, Walter Bright
7  * Standards: ISO/IEC 9899:1999 (E)
8  */
9 module tango.stdc.stdarg;
10 
11 
12 debug(PRINTF) import tango.stdc.stdio: printf;
13 
14 public import core.stdc.stdarg;
15 
16 /+
17 version( GNU )
18 {
19     public import std.c.stdarg;
20 }
21 else version( LDC )
22 {
23     public import ldc.cstdarg;
24 }
25 else
26 {
27 version (X86)
28 {
29     /*********************
30      * The argument pointer type.
31      */
32     alias void* va_list;
33 
34     /**********
35      * Initialize ap.
36      * For 32 bit code, parmn should be the last named parameter.
37      * For 64 bit code, parmn should be __va_argsave.
38      */
39     void va_start(T)(out va_list ap, ref T parmn)
40     {
41         ap = cast(va_list)(cast(void*)&parmn + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1)));
42     }
43 
44     /************
45      * Retrieve and return the next value that is type T.
46      * Should use the other va_arg instead, as this won't work for 64 bit code.
47      */
48     T va_arg(T)(ref va_list ap)
49     {
50         T arg = *cast(T*)ap;
51         ap = cast(va_list)(cast(void*)ap + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1)));
52         return arg;
53     }
54 
55     /************
56      * Retrieve and return the next value that is type T.
57      * This is the preferred version.
58      */
59     void va_arg(T)(ref va_list ap, ref T parmn)
60     {
61         parmn = *cast(T*)ap;
62         ap = cast(va_list)(cast(void*)ap + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1)));
63     }
64 
65     /*************
66      * Retrieve and store through parmn the next value that is of TypeInfo ti.
67      * Used when the static type is not known.
68      */
69     void va_arg()(ref va_list ap, TypeInfo ti, void* parmn)
70     {
71         // Wait until everyone updates to get TypeInfo.talign()
72         //auto talign = ti.talign();
73         //auto p = cast(void*)(cast(size_t)ap + talign - 1) & ~(talign - 1);
74         auto p = ap;
75         auto tsize = ti.tsize();
76         ap = cast(void*)(cast(size_t)p + ((tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1)));
77         parmn[0..tsize] = p[0..tsize];
78     }
79 
80     /***********************
81      * End use of ap.
82      */
83     void va_end(va_list ap)
84     {
85     }
86 
87     void va_copy(out va_list dest, va_list src)
88     {
89         dest = src;
90     }
91 }
92 else version (X86_64)
93 {
94     // Layout of this struct must match __gnuc_va_list for C ABI compatibility
95     struct __va_list
96     {
97         uint offset_regs = 6 * 8;            // no regs
98         uint offset_fpregs = 6 * 8 + 8 * 16; // no fp regs
99         void* stack_args;
100         void* reg_args;
101     }
102 
103     struct __va_argsave_t
104     {
105         size_t[6] regs;   // RDI,RSI,RDX,RCX,R8,R9
106         real[8] fpregs;   // XMM0..XMM7
107         __va_list va;
108     }
109 
110     /*
111      * Making it an array of 1 causes va_list to be passed as a pointer in
112      * function argument lists
113      */
114     alias void* va_list;
115 
116     void va_start(T)(out va_list ap, ref T parmn)
117     {
118         ap = &parmn.va;
119     }
120 
121     T va_arg(T)(va_list ap)
122     {   T a;
123         va_arg(ap, a);
124         return a;
125     }
126 
127     void va_arg(T)(va_list apx, ref T parmn)
128     {
129         debug(PRINTF) printf("va_arg(T) called\n");
130         __va_list* ap = cast(__va_list*)apx;
131         static if (is(T U == __argTypes))
132         {
133             static if (U.length == 0 || T.sizeof > 16 || U[0].sizeof > 8)
134             {   // Always passed in memory
135                 // The arg may have more strict alignment than the stack
136                 auto p = (cast(size_t)ap.stack_args + T.alignof - 1) & ~(T.alignof - 1);
137                 ap.stack_args = cast(void*)(p + ((T.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1)));
138                 parmn = *cast(T*)p;
139             }
140             else static if (U.length == 1)
141             {   // Arg is passed in one register
142                 alias U[0] T1;
143                 static if (is(T1 == double) || is(T1 == float))
144                 {   // Passed in XMM register
145                     if (ap.offset_fpregs < (6 * 8 + 16 * 8))
146                     {
147                         parmn = *cast(T*)(ap.reg_args + ap.offset_fpregs);
148                         ap.offset_fpregs += 16;
149                     }
150                     else
151                     {
152                         parmn = *cast(T*)ap.stack_args;
153                         ap.stack_args += (T1.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
154                     }
155                 }
156                 else
157                 {   // Passed in regular register
158                     if (ap.offset_regs < 6 * 8 && T.sizeof <= 8)
159                     {
160                         parmn = *cast(T*)(ap.reg_args + ap.offset_regs);
161                         ap.offset_regs += 8;
162                     }
163                     else
164                     {
165                         auto p = (cast(size_t)ap.stack_args + T.alignof - 1) & ~(T.alignof - 1);
166                         ap.stack_args = cast(void*)(p + ((T.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1)));
167                         parmn = *cast(T*)p;
168                     }
169                 }
170             }
171             else static if (U.length == 2)
172             {   // Arg is passed in two registers
173                 alias U[0] T1;
174                 alias U[1] T2;
175 
176                 static if (is(T1 == double) || is(T1 == float))
177                 {
178                     if (ap.offset_fpregs < (6 * 8 + 16 * 8))
179                     {
180                         *cast(T1*)&parmn = *cast(T1*)(ap.reg_args + ap.offset_fpregs);
181                         ap.offset_fpregs += 16;
182                     }
183                     else
184                     {
185                         *cast(T1*)&parmn = *cast(T1*)ap.stack_args;
186                         ap.stack_args += (T1.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
187                     }
188                 }
189                 else
190                 {
191                     if (ap.offset_regs < 6 * 8 && T1.sizeof <= 8)
192                     {
193                         *cast(T1*)&parmn = *cast(T1*)(ap.reg_args + ap.offset_regs);
194                         ap.offset_regs += 8;
195                     }
196                     else
197                     {
198                         *cast(T1*)&parmn = *cast(T1*)ap.stack_args;
199                         ap.stack_args += 8;
200                     }
201                 }
202 
203                 auto p = cast(void*)&parmn + 8;
204                 static if (is(T2 == double) || is(T2 == float))
205                 {
206                     if (ap.offset_fpregs < (6 * 8 + 16 * 8))
207                     {
208                         *cast(T2*)p = *cast(T2*)(ap.reg_args + ap.offset_fpregs);
209                         ap.offset_fpregs += 16;
210                     }
211                     else
212                     {
213                         *cast(T2*)p = *cast(T2*)ap.stack_args;
214                         ap.stack_args += (T2.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
215                     }
216                 }
217                 else
218                 {
219                     void* a = void;
220                     if (ap.offset_regs < 6 * 8 && T2.sizeof <= 8)
221                     {
222                         a = ap.reg_args + ap.offset_regs;
223                         ap.offset_regs += 8;
224                     }
225                     else
226                     {
227                         a = ap.stack_args;
228                         ap.stack_args += 8;
229                     }
230                     // Be careful not to go past the size of the actual argument
231                     const sz2 = T.sizeof - 8;
232                     p[0..sz2] = a[0..sz2];
233                 }
234             }
235             else
236             {
237                 static assert(0);
238             }
239         }
240         else
241         {
242             static assert(0, "not a valid argument type for va_arg");
243         }
244     }
245 
246     void va_arg()(va_list apx, TypeInfo ti, void* parmn)
247     {
248         __va_list* ap = cast(__va_list*)apx;
249         TypeInfo arg1, arg2;
250         if (!ti.argTypes(arg1, arg2))
251         {
252             if (arg1 && arg1.tsize() <= 8)
253             {
254                 // Arg is passed in one register
255                 auto tsize = arg1.tsize();
256                 void* p;
257                 auto s = arg1.toString();
258                 if (s == "double" || s == "float" || s == "idouble" || s == "ifloat")
259                 {   // Passed in XMM register
260                     if (ap.offset_fpregs < (6 * 8 + 16 * 8))
261                     {
262                         p = ap.reg_args + ap.offset_fpregs;
263                         ap.offset_fpregs += 16;
264                     }
265                     else
266                     {
267                         p = ap.stack_args;
268                         ap.stack_args += (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
269                     }
270                 }
271                 else
272                 {   // Passed in regular register
273                     if (ap.offset_regs < 6 * 8)
274                     {
275                         p = ap.reg_args + ap.offset_regs;
276                         ap.offset_regs += 8;
277                     }
278                     else
279                     {
280                         p = ap.stack_args;
281                         ap.stack_args += 8;
282                     }
283                 }
284                 parmn[0..tsize] = p[0..tsize];
285 
286                 if (arg2)
287                 {
288                     parmn += 8;
289                     tsize = arg2.tsize();
290                     s = arg2.toString();
291                     if (s == "double" || s == "float" || s == "idouble" || s == "ifloat")
292                     {   // Passed in XMM register
293                         if (ap.offset_fpregs < (6 * 8 + 16 * 8))
294                         {
295                             p = ap.reg_args + ap.offset_fpregs;
296                             ap.offset_fpregs += 16;
297                         }
298                         else
299                         {
300                             p = ap.stack_args;
301                             ap.stack_args += (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
302                         }
303                     }
304                     else
305                     {   // Passed in regular register
306                         if (ap.offset_regs < 6 * 8)
307                         {
308                             p = ap.reg_args + ap.offset_regs;
309                             ap.offset_regs += 8;
310                         }
311                         else
312                         {
313                             p = ap.stack_args;
314                             ap.stack_args += 8;
315                         }
316                     }
317                     tsize = ti.tsize() - 8;
318                     parmn[0..tsize] = p[0..tsize];
319                 }
320             }
321             else
322             {
323                 // Always passed in memory
324                 // The arg may have more strict alignment than the stack
325                 auto talign = ti.talign();
326                 auto tsize = ti.tsize();
327 
328 
329                 auto p = cast(void*)((cast(size_t)ap.stack_args + talign - 1) & ~(talign - 1));
330                 ap.stack_args = cast(void*)(cast(size_t)p + ((tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1)));
331                 parmn[0..tsize] = p[0..tsize];
332             }
333         }
334         else
335         {
336             assert(0, "not a valid argument type for va_arg");
337         }
338     }
339 
340     void va_end(va_list ap)
341     {
342     }
343 
344     void va_copy(out va_list dest, va_list src)
345     {
346         dest = src;
347     }
348 }
349 else
350 {
351     static assert(0);
352 }
353 }
354 +/