summaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/core/stdc/stdarg.d
blob: 646905eaffb4534752e9db8435b78b71b4a77708 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/**
 * D header file for C99.
 *
 * $(C_HEADER_DESCRIPTION pubs.opengroup.org/onlinepubs/009695399/basedefs/_stdarg.h.html, _stdarg.h)
 *
 * Copyright: Copyright Digital Mars 2000 - 2020.
 * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
 * Authors:   Walter Bright, Hauke Duden
 * Standards: ISO/IEC 9899:1999 (E)
 * Source: $(DRUNTIMESRC core/stdc/_stdarg.d)
 */

module core.stdc.stdarg;

@system:
@nogc:
nothrow:

version (X86_64)
{
    version (Windows) { /* different ABI */ }
    else version = SysV_x64;
}

version (GNU)
{
    import gcc.builtins;
}
else version (SysV_x64)
{
    static import core.internal.vararg.sysv_x64;

    version (DigitalMars)
    {
        align(16) struct __va_argsave_t
        {
            size_t[6] regs;   // RDI,RSI,RDX,RCX,R8,R9
            real[8] fpregs;   // XMM0..XMM7
            __va_list va;
        }
    }
}

version (ARM)     version = ARM_Any;
version (AArch64) version = ARM_Any;
version (MIPS32)  version = MIPS_Any;
version (MIPS64)  version = MIPS_Any;
version (PPC)     version = PPC_Any;
version (PPC64)   version = PPC_Any;
version (RISCV32) version = RISCV_Any;
version (RISCV64) version = RISCV_Any;

version (GNU)
{
    // Uses gcc.builtins
}
else version (ARM_Any)
{
    // Darwin uses a simpler varargs implementation
    version (OSX) {}
    else version (iOS) {}
    else version (TVOS) {}
    else version (WatchOS) {}
    else:

    version (ARM)
    {
        version = AAPCS32;
    }
    else version (AArch64)
    {
        version = AAPCS64;
        static import core.internal.vararg.aarch64;
    }
}


T alignUp(size_t alignment = size_t.sizeof, T)(T base) pure
{
    enum mask = alignment - 1;
    static assert(alignment > 0 && (alignment & mask) == 0, "alignment must be a power of 2");
    auto b = cast(size_t) base;
    b = (b + mask) & ~mask;
    return cast(T) b;
}

unittest
{
    assert(1.alignUp == size_t.sizeof);
    assert(31.alignUp!16 == 32);
    assert(32.alignUp!16 == 32);
    assert(33.alignUp!16 == 48);
    assert((-9).alignUp!8 == -8);
}


version (BigEndian)
{
    // Adjusts a size_t-aligned pointer for types smaller than size_t.
    T* adjustForBigEndian(T)(T* p, size_t size) pure
    {
        return size >= size_t.sizeof ? p :
            cast(T*) ((cast(void*) p) + (size_t.sizeof - size));
    }
}


/**
 * The argument pointer type.
 */
version (GNU)
{
    alias va_list = __gnuc_va_list;
    alias __gnuc_va_list = __builtin_va_list;
}
else version (SysV_x64)
{
    alias va_list = core.internal.vararg.sysv_x64.va_list;
    public import core.internal.vararg.sysv_x64 : __va_list, __va_list_tag;
}
else version (AAPCS32)
{
    alias va_list = __va_list;

    // need std::__va_list for C++ mangling compatibility (AAPCS32 section 8.1.4)
    extern (C++, std) struct __va_list
    {
        void* __ap;
    }
}
else version (AAPCS64)
{
    alias va_list = core.internal.vararg.aarch64.va_list;
}
else version (RISCV_Any)
{
    // The va_list type is void*, according to RISCV Calling Convention
    // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc
    alias va_list = void*;
}
else
{
    alias va_list = char*; // incl. unknown platforms
}


/**
 * Initialize ap.
 * parmn should be the last named parameter.
 */
version (GNU)
{
    void va_start(T)(out va_list ap, ref T parmn);
}
else version (LDC)
{
    pragma(LDC_va_start)
    void va_start(T)(out va_list ap, ref T parmn) @nogc;
}
else version (DigitalMars)
{
    version (X86)
    {
        void va_start(T)(out va_list ap, ref T parmn)
        {
            ap = cast(va_list) ((cast(void*) &parmn) + T.sizeof.alignUp);
        }
    }
    else
    {
        void va_start(T)(out va_list ap, ref T parmn); // intrinsic; parmn should be __va_argsave for non-Windows x86_64 targets
    }
}


/**
 * Retrieve and return the next value that is of type T.
 */
version (GNU)
    T va_arg(T)(ref va_list ap); // intrinsic
else
T va_arg(T)(ref va_list ap)
{
    version (X86)
    {
        auto p = cast(T*) ap;
        ap += T.sizeof.alignUp;
        return *p;
    }
    else version (Win64)
    {
        // LDC passes slices as 2 separate 64-bit values, not as 128-bit struct
        version (LDC) enum isLDC = true;
        else          enum isLDC = false;
        static if (isLDC && is(T == E[], E))
        {
            auto p = cast(T*) ap;
            ap += T.sizeof;
            return *p;
        }
        else
        {
            // passed indirectly by value if > 64 bits or of a size that is not a power of 2
            static if (T.sizeof > size_t.sizeof || (T.sizeof & (T.sizeof - 1)) != 0)
                auto p = *cast(T**) ap;
            else
                auto p = cast(T*) ap;
            ap += size_t.sizeof;
            return *p;
        }
    }
    else version (SysV_x64)
    {
        return core.internal.vararg.sysv_x64.va_arg!T(ap);
    }
    else version (AAPCS32)
    {
        // AAPCS32 section 6.5 B.5: type with alignment >= 8 is 8-byte aligned
        // instead of normal 4-byte alignment (APCS doesn't do this).
        if (T.alignof >= 8)
            ap.__ap = ap.__ap.alignUp!8;
        auto p = cast(T*) ap.__ap;
        version (BigEndian)
            static if (T.sizeof < size_t.sizeof)
                p = adjustForBigEndian(p, T.sizeof);
        ap.__ap += T.sizeof.alignUp;
        return *p;
    }
    else version (AAPCS64)
    {
        return core.internal.vararg.aarch64.va_arg!T(ap);
    }
    else version (ARM_Any)
    {
        auto p = cast(T*) ap;
        version (BigEndian)
            static if (T.sizeof < size_t.sizeof)
                p = adjustForBigEndian(p, T.sizeof);
        ap += T.sizeof.alignUp;
        return *p;
    }
    else version (PPC_Any)
    {
        /*
         * The rules are described in the 64bit PowerPC ELF ABI Supplement 1.9,
         * available here:
         * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#PARAM-PASS
         */

        // Chapter 3.1.4 and 3.2.3: alignment may require the va_list pointer to first
        // be aligned before accessing a value
        if (T.alignof >= 8)
            ap = ap.alignUp!8;
        auto p = cast(T*) ap;
        version (BigEndian)
            static if (T.sizeof < size_t.sizeof)
                p = adjustForBigEndian(p, T.sizeof);
        ap += T.sizeof.alignUp;
        return *p;
    }
    else version (MIPS_Any)
    {
        auto p = cast(T*) ap;
        version (BigEndian)
            static if (T.sizeof < size_t.sizeof)
                p = adjustForBigEndian(p, T.sizeof);
        ap += T.sizeof.alignUp;
        return *p;
    }
    else version (RISCV_Any)
    {
        static if (T.sizeof > (size_t.sizeof << 1))
            auto p = *cast(T**) ap;
        else
        {
            static if (T.alignof == (size_t.sizeof << 1))
                ap = ap.alignUp!(size_t.sizeof << 1);
            auto p = cast(T*) ap;
        }
        ap += T.sizeof.alignUp;
        return *p;
    }
    else
        static assert(0, "Unsupported platform");
}


/**
 * Retrieve and store in parmn the next value that is of type T.
 */
version (GNU)
    void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
else
void va_arg(T)(ref va_list ap, ref T parmn)
{
    parmn = va_arg!T(ap);
}


/**
 * End use of ap.
 */
version (GNU)
{
    alias va_end = __builtin_va_end;
}
else version (LDC)
{
    pragma(LDC_va_end)
    void va_end(va_list ap);
}
else version (DigitalMars)
{
    void va_end(va_list ap) {}
}


/**
 * Make a copy of ap.
 */
version (GNU)
{
    alias va_copy = __builtin_va_copy;
}
else version (LDC)
{
    pragma(LDC_va_copy)
    void va_copy(out va_list dest, va_list src);
}
else version (DigitalMars)
{
    version (SysV_x64)
    {
        void va_copy(out va_list dest, va_list src, void* storage = alloca(__va_list_tag.sizeof))
        {
            // Instead of copying the pointers, and aliasing the source va_list,
            // the default argument alloca will allocate storage in the caller's
            // stack frame.  This is still not correct (it should be allocated in
            // the place where the va_list variable is declared) but most of the
            // time the caller's stack frame _is_ the place where the va_list is
            // allocated, so in most cases this will now work.
            dest = cast(va_list) storage;
            *dest = *src;
        }

        import core.stdc.stdlib : alloca;
    }
    else
    {
        void va_copy(out va_list dest, va_list src)
        {
            dest = src;
        }
    }
}