Reputation: 3583
I'm new to gcc inline assembly. Why this code output "1" instead of "5"?
code:
#include <stdio.h>
static inline int atomic_add(volatile int *mem, int add)
{
asm volatile(
"lock xadd %0, (%1);"
: "=a"(add)
: "r"(mem), "a"(add)
: "memory"
);
return add;
}
int main(void)
{
int a=1;
int b=5;
printf ( "%d\n", atomic_add(&a, b) );
return 0;
}
run:
$ ./a.out
1 # why not 5?
many thx. :)
Upvotes: 1
Views: 397
Reputation: 340168
The variable add
stars out with the value 5 and *mem
starts out with 1
.
The lock xadd %0, (%1)
assembly template gets compiled by gcc to:
lock xadd %eax, (%edx)
GCC has to use eax
because your constraints indicate that %0
should use %eax
. Your constraint also ties %eax
to the variable add
. I believe that GCC is free to us whatever register it wants for the other operand (in my test it happened to use %edx
).
So:
%eax
starts with 5
, and %edx
points to a memory location that has the value 1
the xadd
instruction swaps the two operands and places the sum in the destination, so after executing %eax
has 1
and the memory pointed to by
%edxcontains
6`
Your constraint also indicates that %eax
should be stored back into the variable add
, so add
gets 1
. And that is what is returned from the function.
Upvotes: 3
Reputation: 7228
In x86, XADD is the Exchange and Add instruction. So the register holding the add
parameter became 1
after the lock xadd
instruction. add
is then returned by atomic_add()
thus you see 1
printed instead of 5
.
For atomic_add()
you probably want to just use lock add
instead of lock xadd
:
#include <stdio.h>
static inline int atomic_add(volatile int *mem, int add)
{
asm volatile(
"lock add %0, (%1);"
: "=a"(add)
: "r"(mem), "a"(add)
: "memory"
);
return add;
}
int main(void)
{
int a=1;
int b=5;
printf ( "%d\n", atomic_add(&a, b) );
return 0;
}
And this prints 5
like you expect:
$ ./a.out
5
Upvotes: 2