Hi everyone,
I have a question about ARM 32 big endian calling convention. It's about sending small structures as function argument.
For example if passing 3 chars in a structure should alignment in register (argument register for passing those chars in function) be right-adjusted ?
LLVM (trunk version) is passing those chars left-adjusted in register, and trying to read them like they are right-adjusted and there is a problem.
GCC is passing chars in register left-adjusted and works properly.
Here is the example:
#include <stdarg.h>
struct tiny
{
char c;
char d;
char e;
};
f (int n, ...)
{
struct tiny x;
va_list ap;
va_start (ap,n);
x = va_arg (ap,struct tiny);
if (x.c != 10)
abort();
if (x.d != 11)
abort();
if(x.e != 12)
abort();
va_end (ap);
}
main ()
{
struct tiny x[3];
x[0].c = 10;
x[0].d = 11;
x[0].e = 12;
f (3, x[0]);
exit(0);
}
Structs are passed as if loaded from memory by an LDM instruction, and
main is doing that correctly. The problem is in how f is then extracting
the elements from that value.
It looks like this is specifically a bug in clang's handling of va_arg -
if you change f to take a struct tiny instead of a variadic list then it
behaves correctly.
Looking at the -O0 emit-llvm output of clang the va_arg is generated as
%0 = bitcast %struct.__va_list* %ap to i8**
%argp.cur = load i8*, i8** %0, align 4
%argp.next = getelementptr inbounds i8, i8* %argp.cur, i32 4
store i8* %argp.next, i8** %0, align 4
%1 = getelementptr inbounds i8, i8* %argp.cur, i32 1
%2 = bitcast i8* %1 to %struct.tiny*
%3 = bitcast %struct.tiny* %x to i8*
%4 = bitcast %struct.tiny* %2 to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %3, i8* %4, i32 3, i32 1, i1 false)
It's that '%1 = getelementptr inbounds i8, i8* %argp.cur, i32 1' which is
wrong: the offset should be 0 not 1.
John
Thanks for response, I expected something like that, just needed confirmation.
I will make a patch for this soon.
Strahinja