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 (MIPS_Any) 261 { 262 auto p = cast(T*) ap; 263 version (BigEndian) 264 static if (T.sizeof < size_t.sizeof) 265 p = adjustForBigEndian(p, T.sizeof); 266 ap += T.sizeof.alignUp; 267 return *p; 268 } 269 else version (RISCV_Any) 270 { 271 static if (T.sizeof > (size_t.sizeof << 1)) 272 auto p = *cast(T**) ap; 273 else 274 { 275 static if (T.alignof == (size_t.sizeof << 1)) 276 ap = ap.alignUp!(size_t.sizeof << 1); 277 auto p = cast(T*) ap; 278 } 279 ap += T.sizeof.alignUp; 280 return *p; 281 } 282 else 283 static assert(0, "Unsupported platform"); 284 } 285 286 287 /** 288 * Retrieve and store in parmn the next value that is of type T. 289 */ 290 version (GNU) 291 void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic 292 else 293 void va_arg(T)(ref va_list ap, ref T parmn) 294 { 295 parmn = va_arg!T(ap); 296 } 297 298 299 /** 300 * End use of ap. 301 */ 302 version (GNU) 303 { 304 alias va_end = __builtin_va_end; 305 } 306 else version (LDC) 307 { 308 pragma(LDC_va_end) 309 void va_end(va_list ap); 310 } 311 else version (DigitalMars) 312 { 313 void va_end(va_list ap) {} 314 } 315 316 317 /** 318 * Make a copy of ap. 319 */ 320 version (GNU) 321 { 322 alias va_copy = __builtin_va_copy; 323 } 324 else version (LDC) 325 { 326 pragma(LDC_va_copy) 327 void va_copy(out va_list dest, va_list src); 328 } 329 else version (DigitalMars) 330 { 331 version (SysV_x64) 332 { 333 void va_copy(out va_list dest, va_list src, void* storage = alloca(__va_list_tag.sizeof)) 334 { 335 // Instead of copying the pointers, and aliasing the source va_list, 336 // the default argument alloca will allocate storage in the caller's 337 // stack frame. This is still not correct (it should be allocated in 338 // the place where the va_list variable is declared) but most of the 339 // time the caller's stack frame _is_ the place where the va_list is 340 // allocated, so in most cases this will now work. 341 dest = cast(va_list) storage; 342 *dest = *src; 343 } 344 345 import core.stdc.stdlib : alloca; 346 } 347 else 348 { 349 void va_copy(out va_list dest, va_list src) 350 { 351 dest = src; 352 } 353 } 354 }