Nat! bio photo


Senior Mull.

Twitter RSS


va_list is now an array of some (opaque ?) struct... gee thanks

A little trap in the new 64 bit ABI.

void   bar( va_list args)
   va_list   x;  
   x = args;
   return( va_arg( x, int));

does not work anymore. It gives the error

error: array type 'va_list' (aka 'struct __va_list_tag [1]') is not assignable

I was curious if there was any "obvious" change in functionality. Here is the part of a function calling bar disassembled for three different architectures. The disassembly shows, that it's the same internally, two pointers are pushed unto the stack (put into parameter registers).

        movq    %rbx, %rsi
        movl    %r12d, %edi
        call    _bar

        movl    -12(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        call    _bar

        lwz r4,76(r1)
        mr r3,r29
        bl _bar

What does work in the old and the new world is

void   bar( va_list args)
   va_list   *x;  
   x = &args;
   return( va_arg( *x, int));

where I incur one pointer indirection due to syntax. I am still forming an opinion on that...


A photo of Ahruman

From: Ahruman

Isn’t this exactly why va_copy() was introduced? I’d expect it to have zero overhead compared to the assignment with this implementation.

A photo of Nat!

From: Nat!

Very good. I was totally unaware of va_copy. I should have been using that all along.

A photo of Nat!

From: Nat!

Except, that it doesn't work for me. No technically va_copy it's OK. But the language lawyers specify:

Each invocation of va_copy() must be matched by a corresponding invocation of va_end() in the same function.

But the actual real life code, that I am using (now) is:

static void init_parse_context( parse_context *p, unichar *buf, unsigned int len,

va_list args, NSEnumerator *rover)


memset( p, 0, sizeof( parse_context));

p->buf = buf;

p->end = &buf[ len];

// used to be

// p->args = args;

// wants to be

// va_copy( p->args, args)

// has to be

p->args_p = &args;

p->rover = rover;


With the understanding that va_list is valid as long as the stack frame is valid, this used to be no problem. But I certainly can't put va_end at the end of the function, lest it really does something and does some cleanup code.

Post a comment

All comments are held for moderation; basic HTML formatting accepted.

E-mail: (not published)