| /** |
| * 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; |
| |
| @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; |
| } |
| } |
| } |