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