The OpenD Programming Language

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 @nogc:
16 nothrow:
17 
18 version (X86_64)
19 {
20     version (Windows) { /* different ABI */ }
21     else version = SysV_x64;
22 }
23 
24 version (GNU)
25 {
26     import gcc.builtins;
27 }
28 else version (SysV_x64)
29 {
30     static import core.internal.vararg.sysv_x64;
31 
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 }
42 
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;
49 version (RISCV32) version = RISCV_Any;
50 version (RISCV64) version = RISCV_Any;
51 
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 }
75 
76 
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 }
85 
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 }
94 
95 
96 version (BigEndian)
97 {
98     // Adjusts a size_t-aligned pointer for types smaller than size_t.
99     T* adjustForBigEndian(T)(T* p, size_t size) pure
100     {
101         return size >= size_t.sizeof ? p :
102             cast(T*) ((cast(void*) p) + (size_t.sizeof - size));
103     }
104 }
105 
106 
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;
123 
124     // need std::__va_list for C++ mangling compatibility (AAPCS32 section 8.1.4)
125     extern (C++, std) struct __va_list
126     {
127         void* __ap;
128     }
129 }
130 else version (AAPCS64)
131 {
132     alias va_list = core.internal.vararg.aarch64.va_list;
133 }
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 }
140 else
141 {
142     alias va_list = char*; // incl. unknown platforms
143 }
144 
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)
162     {
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         }
167     }
168     else
169     {
170         void va_start(T)(out va_list ap, ref T parmn); // intrinsic; parmn should be __va_argsave for non-Windows x86_64 targets
171     }
172 }
173 
174 
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)
184     {
185         auto p = cast(T*) ap;
186         ap += T.sizeof.alignUp;
187         return *p;
188     }
189     else version (Win64)
190     {
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         }
200         else
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         }
210     }
211     else version (SysV_x64)
212     {
213         return core.internal.vararg.sysv_x64.va_arg!T(ap);
214     }
215     else version (AAPCS32)
216     {
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;
227     }
228     else version (AAPCS64)
229     {
230         return core.internal.vararg.aarch64.va_arg!T(ap);
231     }
232     else version (ARM_Any)
233     {
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;
240     }
241     else version (PPC_Any)
242     {
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;
259     }
260     else version (LoongArch64)
261     {
262         auto p = cast(T*) ap;
263         ap += T.sizeof.alignUp;
264         return *p;
265     }
266     else version (MIPS_Any)
267     {
268         auto p = cast(T*) ap;
269         version (BigEndian)
270             static if (T.sizeof < size_t.sizeof)
271                 p = adjustForBigEndian(p, T.sizeof);
272         ap += T.sizeof.alignUp;
273         return *p;
274     }
275     else version (RISCV_Any)
276     {
277         static if (T.sizeof > (size_t.sizeof << 1))
278             auto p = *cast(T**) ap;
279         else
280         {
281             static if (T.alignof == (size_t.sizeof << 1))
282                 ap = ap.alignUp!(size_t.sizeof << 1);
283             auto p = cast(T*) ap;
284         }
285         ap += T.sizeof.alignUp;
286         return *p;
287     }
288     else
289         static assert(0, "Unsupported platform");
290 }
291 
292 
293 /**
294  * Retrieve and store in parmn the next value that is of type T.
295  */
296 version (GNU)
297     void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
298 else
299 void va_arg(T)(ref va_list ap, ref T parmn)
300 {
301     parmn = va_arg!T(ap);
302 }
303 
304 
305 /**
306  * End use of ap.
307  */
308 version (GNU)
309 {
310     alias va_end = __builtin_va_end;
311 }
312 else version (LDC)
313 {
314     pragma(LDC_va_end)
315     void va_end(va_list ap);
316 }
317 else version (DigitalMars)
318 {
319     void va_end(va_list ap) {}
320 }
321 
322 
323 /**
324  * Make a copy of ap.
325  */
326 version (GNU)
327 {
328     alias va_copy = __builtin_va_copy;
329 }
330 else version (LDC)
331 {
332     pragma(LDC_va_copy)
333     void va_copy(out va_list dest, va_list src);
334 }
335 else version (DigitalMars)
336 {
337     version (SysV_x64)
338     {
339         void va_copy(out va_list dest, va_list src, void* storage = alloca(__va_list_tag.sizeof))
340         {
341             // Instead of copying the pointers, and aliasing the source va_list,
342             // the default argument alloca will allocate storage in the caller's
343             // stack frame.  This is still not correct (it should be allocated in
344             // the place where the va_list variable is declared) but most of the
345             // time the caller's stack frame _is_ the place where the va_list is
346             // allocated, so in most cases this will now work.
347             dest = cast(va_list) storage;
348             *dest = *src;
349         }
350 
351         import core.stdc.stdlib : alloca;
352     }
353     else
354     {
355         void va_copy(out va_list dest, va_list src)
356         {
357             dest = src;
358         }
359     }
360 }