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
13module core.stdc.stdarg;
14
15@system:
16@nogc:
17nothrow:
18
19version (X86_64)
20{
21    version (Windows) { /* different ABI */ }
22    else version = SysV_x64;
23}
24
25version (GNU)
26{
27    import gcc.builtins;
28}
29else 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
44version (ARM)     version = ARM_Any;
45version (AArch64) version = ARM_Any;
46version (MIPS32)  version = MIPS_Any;
47version (MIPS64)  version = MIPS_Any;
48version (PPC)     version = PPC_Any;
49version (PPC64)   version = PPC_Any;
50
51version (GNU)
52{
53    // Uses gcc.builtins
54}
55else 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
76T 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
85unittest
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
95version (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 */
109version (GNU)
110{
111    alias va_list = __gnuc_va_list;
112    alias __gnuc_va_list = __builtin_va_list;
113}
114else 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}
119else 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}
129else version (AAPCS64)
130{
131    alias va_list = core.internal.vararg.aarch64.va_list;
132}
133else
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 */
143version (GNU)
144{
145    void va_start(T)(out va_list ap, ref T parmn);
146}
147else version (LDC)
148{
149    pragma(LDC_va_start)
150    void va_start(T)(out va_list ap, ref T parmn) @nogc;
151}
152else 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 */
171version (GNU)
172    T va_arg(T)(ref va_list ap); // intrinsic
173else
174T 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 */
270version (GNU)
271    void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
272else
273void 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 */
282version (GNU)
283{
284    alias va_end = __builtin_va_end;
285}
286else version (LDC)
287{
288    pragma(LDC_va_end)
289    void va_end(va_list ap);
290}
291else version (DigitalMars)
292{
293    void va_end(va_list ap) {}
294}
295
296
297/**
298 * Make a copy of ap.
299 */
300version (GNU)
301{
302    alias va_copy = __builtin_va_copy;
303}
304else version (LDC)
305{
306    pragma(LDC_va_copy)
307    void va_copy(out va_list dest, va_list src);
308}
309else 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