This is the mail archive of the
gcc-help@gcc.gnu.org
mailing list for the GCC project.
AMD64 ABI and comparison of structures
- From: José Bollo <jose dot bollo at tele2 dot fr>
- To: gcc-help at gcc dot gnu dot org
- Date: Thu, 25 May 2006 12:47:20 +0200
- Subject: AMD64 ABI and comparison of structures
hi all,
I repost here a problem that I met using GCC 3.4.4 and ABI for AMD64
(-march=athlon64).
the following C code
typedef struct {
int32_t a;
void *b;
} S;
void fs() {
char t[255];
int i;
for(i=0;i<255;i++)
t[i]=(char)(i+1);
}
int cmp1(S a1, S a2) {
return !memcmp(&a1,&a2,sizeof(S));
}
int cmp2(S a1, S a2) {
return a1.a==a2.a && a1.b==a2.b;
}
void put(S y) {
int a,b;
S x;
x=y;
a=cmp1(x,y);
b=cmp2(x,y);
printf("a=%d b=%d\n",a,b);
}
int main() {
S a;
a.a=1;
a.b=&a;
fs();
put(a);
return 0;
}
compiled for my linux x86_64 using the amd64 ABI (-march=athlon64) with gcc
3.4.4 prints "a=0 b=1"!
i have seen why and it is clear to me how a such result is produced.
I add below a commented assembly listing of the incriminated part.
i think that it is not a bug and that the structure can not be compared using
'memcmp'.
do you agree or it a bug ?
is it a bug or is it normal?
if it is a bug, must i open a bug track?
please comment.
regards
josé
First, the "put" function. After the obligatory initialisation code...
> .globl put
> .type put, @function
> put:
> .LFB8:
> pushq %rbp
> .LCFI8:
> movq %rsp, %rbp
> .LCFI9:
> subq $48, %rsp
> .LCFI10:
The parameter (y) was passed in edi:rsi. Here it is saved on the stack.
Presumably this is done to make debugging easier. 96 bits are copied.
> movl %edi, -16(%rbp)
> movq %rsi, -8(%rbp)
This makes a copy of the parameter on the stack. This is the translation
of the 'x=y' instruction. This time 128 bits are copied (why?) which
means that, if we called memcmp(&x,&y,sizeof(S)) right now, it would
return 0 (i.e. they are the same). But...
> movq -16(%rbp), %rax
> movq %rax, -48(%rbp)
> movq -8(%rbp), %rax
> movq %rax, -40(%rbp)
Here, cmp1 is called. As we already saw, only the meaningful part of the
structure is passed, in register (edx:rcx for one parameter, edi:rsi for
the other one). The result is saved on the stack (local var a)
> movl -16(%rbp), %edx
> movq -8(%rbp), %rcx
> movl -48(%rbp), %edi
> movq -40(%rbp), %rsi
> call cmp1
> movl %eax, -20(%rbp)
This time, cmp2 is called and the result is saved in b
> movl -16(%rbp), %edx
> movq -8(%rbp), %rcx
> movl -48(%rbp), %edi
> movq -40(%rbp), %rsi
> call cmp2
> movl %eax, -24(%rbp)
> movl -24(%rbp), %edx
> movl -20(%rbp), %esi
> movl $.LC0, %edi
> movl $0, %eax
> call printf
> leave
> ret
> .LFE8:
> .size put, .-put
Now it should be clear why cmp1 isn't working. Let's have a look
Entry code
> .globl cmp1
> .type cmp1, @function
> cmp1:
> .LFB6:
> pushq %rbp
> .LCFI3:
> movq %rsp, %rbp
> .LCFI4:
> subq $48, %rsp
save the parameters on the stack (one structure = 96 bits of data + 32
bits of junk)
> .LCFI5:
> movl %edi, -16(%rbp)
> movq %rsi, -8(%rbp)
> movl %edx, -32(%rbp)
> movq %rcx, -24(%rbp)
compare 128 bits of "data" on the stack. Obviously this won't work.
(I'll presume a call to memcmp was generated rather than rep cmpsq
because optimisations were turned off)
> leaq -32(%rbp), %rsi
> leaq -16(%rbp), %rdi
> movl $16, %edx
> call memcmp
> movl %eax, -36(%rbp)
> cmpl $0, -36(%rbp)
> sete %al
> movzbl %al, %eax
> movl %eax, -36(%rbp)
> movl -36(%rbp), %eax
> leave
> ret
> .LFE6:
> .size cmp1, .-cmp1
> .globl cmp2
> .type cmp2, @function
Obviously, cmp2 works because it only compares 96 bits of data
> cmp2:
> .LFB7:
> pushq %rbp
> .LCFI6:
> movq %rsp, %rbp
> .LCFI7:
> movl %edi, -16(%rbp)
> movq %rsi, -8(%rbp)
> movl %edx, -32(%rbp)
> movq %rcx, -24(%rbp)
> movl $0, -36(%rbp)
> movl -16(%rbp), %eax
> cmpl -32(%rbp), %eax
> jne .L7
> movq -8(%rbp), %rax
> cmpq -24(%rbp), %rax
> jne .L7
> movl $1, -36(%rbp)
> .L7:
> movl -36(%rbp), %eax
> leave
> ret
> .LFE7:
> .size cmp2, .-cmp2