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