Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Commit 7bd091f

Browse files
committed
Experimental generic vararg ABI fix & clean-up.
System V AMD64 ABI still unsupported because I can't experiment with it.
1 parent fe8e8a9 commit 7bd091f

File tree

2 files changed

+118
-349
lines changed

2 files changed

+118
-349
lines changed

src/core/stdc/stdarg.d

+117-203
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ version ( PPC64 ) version = AnyPPC;
2020

2121
version( X86_64 )
2222
{
23-
version( LDC ) version = LDC_X86_64;
24-
2523
// Determine if type is a vector type
2624
template isVectorType(T)
2725
{
@@ -295,7 +293,121 @@ version( X86_64 )
295293
}
296294
}
297295

298-
version( X86 )
296+
version( LDC )
297+
{
298+
// FIXME: This isn't actually tested at all for ARM.
299+
// Really struct va_list { void* ptr; }, but for compatibility with
300+
// x86-style code that uses char*, we just define it as the raw pointer.
301+
302+
version( AnyPPC )
303+
alias void* va_list;
304+
else
305+
alias char* va_list;
306+
307+
pragma(LDC_va_start)
308+
void va_start(T)(va_list ap, ref T);
309+
310+
private pragma(LDC_va_arg)
311+
T va_arg_impl(T)(va_list ap);
312+
313+
T va_arg(T)(ref va_list ap)
314+
{
315+
version( Win64 )
316+
{
317+
static if (T.sizeof > size_t.sizeof || (T.sizeof & (T.sizeof - 1)) != 0)
318+
T arg = **cast(T**)ap;
319+
else
320+
T arg = *cast(T*)ap;
321+
ap += size_t.sizeof;
322+
return arg;
323+
}
324+
else version( ARM )
325+
{
326+
T arg = *cast(T*)ap;
327+
ap += (T.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
328+
return arg;
329+
}
330+
else
331+
return va_arg_impl!T(ap);
332+
}
333+
334+
void va_arg(T)(ref va_list ap, ref T parmn)
335+
{
336+
version( Win64 )
337+
{
338+
static if (T.sizeof > size_t.sizeof || (T.sizeof & (T.sizeof - 1)) != 0)
339+
parmn = **cast(T**)ap;
340+
else
341+
parmn = *cast(T*)ap;
342+
ap += size_t.sizeof;
343+
}
344+
else version( ARM )
345+
{
346+
parmn = *cast(T*)ap;
347+
ap += (T.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
348+
}
349+
else
350+
parmn = va_arg!T(ap);
351+
}
352+
353+
void va_arg()(ref va_list ap, TypeInfo ti, void* parmn)
354+
{
355+
auto tsize = ti.tsize;
356+
357+
version( X86 )
358+
{
359+
// Wait until everyone updates to get TypeInfo.talign
360+
//auto talign = ti.talign;
361+
//auto p = cast(va_list) ((cast(size_t)ap + talign - 1) & ~(talign - 1));
362+
auto p = ap;
363+
ap = p + ((tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1));
364+
}
365+
else version( Win64 )
366+
{
367+
// passed byval if > 64 bits or not a power of 2
368+
auto p = (tsize > size_t.sizeof || (tsize & (tsize - 1)) != 0) ? *cast(char**)ap : ap;
369+
ap += size_t.sizeof;
370+
}
371+
else version( X86_64 )
372+
{
373+
static assert(false, "core.stdc.stdarg.va_arg() not yet implemented for System V AMD64 ABI");
374+
}
375+
else version( ARM )
376+
{
377+
// Wait until everyone updates to get TypeInfo.talign
378+
//auto talign = ti.talign;
379+
//auto p = cast(va_list) ((cast(size_t)ap + talign - 1) & ~(talign - 1));
380+
auto p = ap;
381+
ap = p + ((tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1));
382+
}
383+
else version( AnyPPC )
384+
{
385+
/*
386+
* The rules are described in the 64bit PowerPC ELF ABI Supplement 1.9,
387+
* available here:
388+
* http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#PARAM-PASS
389+
*/
390+
391+
// This works for all types because only the rules for non-floating,
392+
// non-vector types are used.
393+
auto p = (tsize < size_t.sizeof ? ap + (size_t.sizeof - tsize) : ap);
394+
ap += (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
395+
}
396+
else
397+
{
398+
static assert(false, "Unsupported platform");
399+
}
400+
401+
parmn[0..tsize] = (cast(void*)p)[0..tsize];
402+
}
403+
404+
pragma(LDC_va_end)
405+
void va_end(va_list ap);
406+
407+
pragma(LDC_va_copy)
408+
void va_copy(out va_list dest, va_list src);
409+
}
410+
else version( X86 )
299411
{
300412
/*********************
301413
* The argument pointer type.
@@ -307,17 +419,9 @@ version( X86 )
307419
* For 32 bit code, parmn should be the last named parameter.
308420
* For 64 bit code, parmn should be __va_argsave.
309421
*/
310-
version(LDC)
311-
{
312-
pragma(LDC_va_start)
313-
void va_start(T)(va_list ap, ref T);
314-
}
315-
else
422+
void va_start(T)(out va_list ap, ref T parmn)
316423
{
317-
void va_start(T)(out va_list ap, ref T parmn)
318-
{
319-
ap = cast(va_list)( cast(void*) &parmn + ( ( T.sizeof + int.sizeof - 1 ) & ~( int.sizeof - 1 ) ) );
320-
}
424+
ap = cast(va_list)( cast(void*) &parmn + ( ( T.sizeof + int.sizeof - 1 ) & ~( int.sizeof - 1 ) ) );
321425
}
322426

323427
/************
@@ -368,151 +472,6 @@ version( X86 )
368472
dest = src;
369473
}
370474
}
371-
else version( ARM )
372-
{
373-
// FIXME: This isn't actually tested at all.
374-
// Really struct va_list { void* ptr; }, but for compatibility with
375-
// x86-style code that uses char*, we just define it as the raw pointer.
376-
alias va_list = char*;
377-
378-
version(LDC)
379-
{
380-
pragma(LDC_va_start)
381-
void va_start(T)(va_list ap, ref T);
382-
}
383-
else static assert("Unsupported platform.");
384-
385-
/**
386-
* Retrieve and return the next value that is type T.
387-
* This is the preferred version.
388-
*/
389-
void va_arg(T)(ref va_list ap, ref T parmn)
390-
{
391-
parmn = *cast(T*)ap;
392-
ap = cast(va_list)(cast(void*)ap + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1)));
393-
}
394-
395-
/**
396-
* Retrieve and store through parmn the next value that is of TypeInfo ti.
397-
* Used when the static type is not known.
398-
*/
399-
void va_arg()(ref va_list ap, TypeInfo ti, void* parmn)
400-
{
401-
// Wait until everyone updates to get TypeInfo.talign
402-
//auto talign = ti.talign;
403-
//auto p = cast(void*)(cast(size_t)ap + talign - 1) & ~(talign - 1);
404-
auto p = *cast(void**) &ap;
405-
auto tsize = ti.tsize;
406-
*cast(void**) &ap += ((tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1));
407-
parmn[0..tsize] = p[0..tsize];
408-
}
409-
410-
/**
411-
* End use of ap.
412-
*/
413-
void va_end(va_list ap)
414-
{
415-
}
416-
417-
void va_copy(out va_list dest, va_list src)
418-
{
419-
dest = src;
420-
}
421-
}
422-
else version ( LDC_X86_64 )
423-
{
424-
version ( Win64 )
425-
{
426-
alias char* va_list;
427-
428-
pragma(LDC_va_start)
429-
void va_start(T)(va_list ap, ref T);
430-
431-
T va_arg(T)(ref va_list ap)
432-
{
433-
static if (T.sizeof > size_t.sizeof || (T.sizeof & (T.sizeof - 1)) != 0)
434-
T arg = **cast(T**)ap;
435-
else
436-
T arg = *cast(T*)ap;
437-
ap += size_t.sizeof;
438-
return arg;
439-
}
440-
441-
void va_arg(T)(ref va_list ap, ref T parmn)
442-
{
443-
static if (T.sizeof > size_t.sizeof || (T.sizeof & (T.sizeof - 1)) != 0)
444-
parmn = **cast(T**)ap;
445-
else
446-
parmn = *cast(T*)ap;
447-
ap += size_t.sizeof;
448-
}
449-
450-
void va_arg()(ref va_list ap, TypeInfo ti, void* parmn)
451-
{
452-
// Wait until everyone updates to get TypeInfo.talign
453-
//auto talign = ti.talign;
454-
//auto p = (ap + talign - 1) & ~(talign - 1);
455-
auto p = ap;
456-
ap = p + size_t.sizeof;
457-
auto tsize = ti.tsize;
458-
void* q = (tsize > size_t.sizeof || (tsize & (tsize - 1)) != 0) ? *cast(void**)p : cast(void*)p;
459-
parmn[0..tsize] = q[0..tsize];
460-
}
461-
462-
void va_end(va_list ap)
463-
{
464-
}
465-
466-
void va_copy(out va_list dest, va_list src)
467-
{
468-
dest = src;
469-
}
470-
}
471-
else
472-
{
473-
// We absolutely need va_list to be something that causes the actual struct
474-
// to be passed by reference for compatibility with C function declarations.
475-
// Otherwise, e.g. "extern(C) int vprintf(const char*, va_list)" would fail
476-
// horribly. Now, with va_list = __va_list*, we can't properly implement
477-
// va_copy without cheating, as it needs to save the struct contents
478-
// somewhere (offset_regs, offset_fpregs), copying a pointer does not help.
479-
//
480-
// Currently, we just special-case va_start/va_copy when lowering the
481-
// pragmas and make them allocate the struct on the caller's stack. This
482-
// gets us 99% there for the common use cases. Eventually, the whole mess
483-
// will have to be cleaned up (also in DMD), probably by making va_list a
484-
// magic built-in type that on x86_64 decays to a reference like the actual
485-
// definition in C (one-element array), or by giving va_copy a different
486-
// signature.
487-
alias va_list = __va_list*;
488-
489-
pragma(LDC_va_start)
490-
void va_start(T)(out va_list ap, ref T);
491-
492-
T va_arg(T)(va_list ap)
493-
{
494-
T a;
495-
va_arg(ap, a);
496-
return a;
497-
}
498-
499-
void va_arg(T)(va_list apx, ref T parmn)
500-
{
501-
va_arg_x86_64(apx, parmn);
502-
}
503-
504-
void va_arg()(va_list apx, TypeInfo ti, void* parmn)
505-
{
506-
va_arg_x86_64(apx, ti, parmn);
507-
}
508-
509-
pragma(LDC_va_end)
510-
void va_end(va_list ap);
511-
512-
pragma(LDC_va_copy)
513-
void va_copy(out va_list dest, va_list src);
514-
}
515-
}
516475
else version (Windows) // Win64
517476
{ /* Win64 is characterized by all arguments fitting into a register size.
518477
* Smaller ones are padded out to register size, and larger ones are passed by
@@ -634,51 +593,6 @@ else version ( X86_64 )
634593
dest = src;
635594
}
636595
}
637-
else version ( AnyPPC )
638-
{
639-
version ( LDC )
640-
{
641-
/*
642-
* The rules are described in the 64bit PowerPC ELF ABI Supplement 1.9,
643-
* available here:
644-
* http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#PARAM-PASS
645-
*/
646-
647-
alias void *va_list;
648-
649-
pragma(LDC_va_start)
650-
void va_start(T)(va_list ap, ref T);
651-
652-
private pragma(LDC_va_arg)
653-
T va_arg_impl(T)(va_list ap);
654-
655-
T va_arg(T)(ref va_list ap)
656-
{
657-
return va_arg_impl!T(ap);
658-
}
659-
660-
void va_arg(T)(ref va_list ap, ref T parmn)
661-
{
662-
parmn = va_arg!T(ap);
663-
}
664-
665-
void va_arg()(ref va_list ap, TypeInfo ti, void* parmn)
666-
{
667-
// This works for all types because only the rules for non-floating,
668-
// non-vector types are used.
669-
auto tsize = ti.tsize();
670-
auto p = tsize < size_t.sizeof ? cast(void*)(cast(void*)ap + (size_t.sizeof - tsize)) : ap;
671-
ap = cast(va_list)(cast(void*)ap + ((tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1)));
672-
parmn[0..tsize] = p[0..tsize];
673-
}
674-
675-
pragma(LDC_va_end)
676-
void va_end(va_list ap);
677-
678-
pragma(LDC_va_copy)
679-
void va_copy(out va_list dest, va_list src);
680-
}
681-
}
682596
else
683597
{
684598
static assert(false, "Unsupported platform");

0 commit comments

Comments
 (0)