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