Strahinja Petrovic via llvm-dev
2016-Jun-17 10:22 UTC
[llvm-dev] question about ARM 32 big endian
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> -----Original Message----- > From: llvm-dev [mailto:llvm-dev-bounces at lists.llvm.org] On Behalf Of > Strahinja Petrovic via llvm-dev > Sent: 17 June 2016 11:22 > To: llvm-dev at lists.llvm.org > Subject: [llvm-dev] question about ARM 32 big endian > > 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); > } > > > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Strahinja Petrovic via llvm-dev
2016-Jun-17 13:31 UTC
[llvm-dev] question about ARM 32 big endian
Thanks for response, I expected something like that, just needed confirmation. I will make a patch for this soon. Strahinja On 17.06.2016. 15:18, John Brawn wrote:> 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 > >> -----Original Message----- >> From: llvm-dev [mailto:llvm-dev-bounces at lists.llvm.org] On Behalf Of >> Strahinja Petrovic via llvm-dev >> Sent: 17 June 2016 11:22 >> To: llvm-dev at lists.llvm.org >> Subject: [llvm-dev] question about ARM 32 big endian >> >> 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); >> } >> >> >> >> _______________________________________________ >> LLVM Developers mailing list >> llvm-dev at lists.llvm.org >> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev