1 /**
2  * D header file for C99.
3  *
4  * $(C_HEADER_DESCRIPTION pubs.opengroup.org/onlinepubs/009695399/basedefs/_stdarg.h.html, _stdarg.h)
5  *
6  * Copyright: Copyright Digital Mars 2000 - 2020.
7  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
8  * Authors:   Walter Bright, Hauke Duden
9  * Standards: ISO/IEC 9899:1999 (E)
10  * Source: $(DRUNTIMESRC core/stdc/_stdarg.d)
11  */
12 
13 module core.stdc.stdarg;
14 
15 @system:
16 @nogc:
17 nothrow:
18 
version(X86_64)19 version (X86_64)
20 {
21     version (Windows) { /* different ABI */ }
22     else version = SysV_x64;
23 }
24 
version(GNU)25 version (GNU)
26 {
27     import gcc.builtins;
28 }
version(SysV_x64)29 else version (SysV_x64)
30 {
31     static import core.internal.vararg.sysv_x64;
32 
33     version (DigitalMars)
34     {
35         align(16) struct __va_argsave_t
36         {
37             size_t[6] regs;   // RDI,RSI,RDX,RCX,R8,R9
38             real[8] fpregs;   // XMM0..XMM7
39             __va_list va;
40         }
41     }
42 }
43 
44 version (ARM)     version = ARM_Any;
45 version (AArch64) version = ARM_Any;
46 version (MIPS32)  version = MIPS_Any;
47 version (MIPS64)  version = MIPS_Any;
48 version (PPC)     version = PPC_Any;
49 version (PPC64)   version = PPC_Any;
50 
51 version (GNU)
52 {
53     // Uses gcc.builtins
54 }
55 else version (ARM_Any)
56 {
57     // Darwin uses a simpler varargs implementation
58     version (OSX) {}
59     else version (iOS) {}
60     else version (TVOS) {}
61     else version (WatchOS) {}
62     else:
63 
64     version (ARM)
65     {
66         version = AAPCS32;
67     }
68     else version (AArch64)
69     {
70         version = AAPCS64;
71         static import core.internal.vararg.aarch64;
72     }
73 }
74 
75 
76 T alignUp(size_t alignment = size_t.sizeof, T)(T base) pure
77 {
78     enum mask = alignment - 1;
79     static assert(alignment > 0 && (alignment & mask) == 0, "alignment must be a power of 2");
80     auto b = cast(size_t) base;
81     b = (b + mask) & ~mask;
82     return cast(T) b;
83 }
84 
85 unittest
86 {
87     assert(1.alignUp == size_t.sizeof);
88     assert(31.alignUp!16 == 32);
89     assert(32.alignUp!16 == 32);
90     assert(33.alignUp!16 == 48);
91     assert((-9).alignUp!8 == -8);
92 }
93 
94 
95 version (BigEndian)
96 {
97     // Adjusts a size_t-aligned pointer for types smaller than size_t.
98     T* adjustForBigEndian(T)(T* p, size_t size) pure
99     {
100         return size >= size_t.sizeof ? p :
101             cast(T*) ((cast(void*) p) + (size_t.sizeof - size));
102     }
103 }
104 
105 
106 /**
107  * The argument pointer type.
108  */
109 version (GNU)
110 {
111     alias va_list = __gnuc_va_list;
112     alias __gnuc_va_list = __builtin_va_list;
113 }
114 else version (SysV_x64)
115 {
116     alias va_list = core.internal.vararg.sysv_x64.va_list;
117     public import core.internal.vararg.sysv_x64 : __va_list, __va_list_tag;
118 }
119 else version (AAPCS32)
120 {
121     alias va_list = __va_list;
122 
123     // need std::__va_list for C++ mangling compatibility (AAPCS32 section 8.1.4)
124     extern (C++, std) struct __va_list
125     {
126         void* __ap;
127     }
128 }
129 else version (AAPCS64)
130 {
131     alias va_list = core.internal.vararg.aarch64.va_list;
132 }
133 else
134 {
135     alias va_list = char*; // incl. unknown platforms
136 }
137 
138 
139 /**
140  * Initialize ap.
141  * parmn should be the last named parameter.
142  */
143 version (GNU)
144 {
145     void va_start(T)(out va_list ap, ref T parmn);
146 }
147 else version (LDC)
148 {
149     pragma(LDC_va_start)
150     void va_start(T)(out va_list ap, ref T parmn) @nogc;
151 }
152 else version (DigitalMars)
153 {
154     version (X86)
155     {
156         void va_start(T)(out va_list ap, ref T parmn)
157         {
158             ap = cast(va_list) ((cast(void*) &parmn) + T.sizeof.alignUp);
159         }
160     }
161     else
162     {
163         void va_start(T)(out va_list ap, ref T parmn); // intrinsic; parmn should be __va_argsave for non-Windows x86_64 targets
164     }
165 }
166 
167 
168 /**
169  * Retrieve and return the next value that is of type T.
170  */
171 version (GNU)
172     T va_arg(T)(ref va_list ap); // intrinsic
173 else
174 T va_arg(T)(ref va_list ap)
175 {
176     version (X86)
177     {
178         auto p = cast(T*) ap;
179         ap += T.sizeof.alignUp;
180         return *p;
181     }
182     else version (Win64)
183     {
184         // LDC passes slices as 2 separate 64-bit values, not as 128-bit struct
185         version (LDC) enum isLDC = true;
186         else          enum isLDC = false;
187         static if (isLDC && is(T == E[], E))
188         {
189             auto p = cast(T*) ap;
190             ap += T.sizeof;
191             return *p;
192         }
193         else
194         {
195             // passed indirectly by value if > 64 bits or of a size that is not a power of 2
196             static if (T.sizeof > size_t.sizeof || (T.sizeof & (T.sizeof - 1)) != 0)
197                 auto p = *cast(T**) ap;
198             else
199                 auto p = cast(T*) ap;
200             ap += size_t.sizeof;
201             return *p;
202         }
203     }
204     else version (SysV_x64)
205     {
206         return core.internal.vararg.sysv_x64.va_arg!T(ap);
207     }
208     else version (AAPCS32)
209     {
210         // AAPCS32 section 6.5 B.5: type with alignment >= 8 is 8-byte aligned
211         // instead of normal 4-byte alignment (APCS doesn't do this).
212         if (T.alignof >= 8)
213             ap.__ap = ap.__ap.alignUp!8;
214         auto p = cast(T*) ap.__ap;
215         version (BigEndian)
216             static if (T.sizeof < size_t.sizeof)
217                 p = adjustForBigEndian(p, T.sizeof);
218         ap.__ap += T.sizeof.alignUp;
219         return *p;
220     }
221     else version (AAPCS64)
222     {
223         return core.internal.vararg.aarch64.va_arg!T(ap);
224     }
225     else version (ARM_Any)
226     {
227         auto p = cast(T*) ap;
228         version (BigEndian)
229             static if (T.sizeof < size_t.sizeof)
230                 p = adjustForBigEndian(p, T.sizeof);
231         ap += T.sizeof.alignUp;
232         return *p;
233     }
234     else version (PPC_Any)
235     {
236         /*
237          * The rules are described in the 64bit PowerPC ELF ABI Supplement 1.9,
238          * available here:
239          * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#PARAM-PASS
240          */
241 
242         // Chapter 3.1.4 and 3.2.3: alignment may require the va_list pointer to first
243         // be aligned before accessing a value
244         if (T.alignof >= 8)
245             ap = ap.alignUp!8;
246         auto p = cast(T*) ap;
247         version (BigEndian)
248             static if (T.sizeof < size_t.sizeof)
249                 p = adjustForBigEndian(p, T.sizeof);
250         ap += T.sizeof.alignUp;
251         return *p;
252     }
253     else version (MIPS_Any)
254     {
255         auto p = cast(T*) ap;
256         version (BigEndian)
257             static if (T.sizeof < size_t.sizeof)
258                 p = adjustForBigEndian(p, T.sizeof);
259         ap += T.sizeof.alignUp;
260         return *p;
261     }
262     else
263         static assert(0, "Unsupported platform");
264 }
265 
266 
267 /**
268  * Retrieve and store in parmn the next value that is of type T.
269  */
270 version (GNU)
271     void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
272 else
273 void va_arg(T)(ref va_list ap, ref T parmn)
274 {
275     parmn = va_arg!T(ap);
276 }
277 
278 
279 /**
280  * End use of ap.
281  */
282 version (GNU)
283 {
284     alias va_end = __builtin_va_end;
285 }
286 else version (LDC)
287 {
288     pragma(LDC_va_end)
289     void va_end(va_list ap);
290 }
291 else version (DigitalMars)
292 {
293     void va_end(va_list ap) {}
294 }
295 
296 
297 /**
298  * Make a copy of ap.
299  */
300 version (GNU)
301 {
302     alias va_copy = __builtin_va_copy;
303 }
304 else version (LDC)
305 {
306     pragma(LDC_va_copy)
307     void va_copy(out va_list dest, va_list src);
308 }
309 else version (DigitalMars)
310 {
311     version (SysV_x64)
312     {
313         void va_copy(out va_list dest, va_list src, void* storage = alloca(__va_list_tag.sizeof))
314         {
315             // Instead of copying the pointers, and aliasing the source va_list,
316             // the default argument alloca will allocate storage in the caller's
317             // stack frame.  This is still not correct (it should be allocated in
318             // the place where the va_list variable is declared) but most of the
319             // time the caller's stack frame _is_ the place where the va_list is
320             // allocated, so in most cases this will now work.
321             dest = cast(va_list) storage;
322             *dest = *src;
323         }
324 
325         import core.stdc.stdlib : alloca;
326     }
327     else
328     {
329         void va_copy(out va_list dest, va_list src)
330         {
331             dest = src;
332         }
333     }
334 }
335