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