Consider this program, which can be compiled as either 32-bit or 64-bit:
#include <stdio.h>static int f(int x, int y) { __asm__("shrl $4, %0\n\t""movl %1, %%edx\n\t""addl %%edx, %0" : "+r"(x) // needs "+&r" to work as intended : "r"(y) : "edx" ); return x;}int main(void) { printf("0x%08X\n", f(0x10000000, 0x10000000));}
At -O1
or higher, it gives the wrong answer (0x02000000
instead of 0x11000000
), because x
gets written before y
gets read, but the constraint for x
doesn't have the &
to specify earlyclobber, so the compiler put them in the same register. If I change +r
to +&r
, then it gives the right answer again, as expected.
Now consider this program:
#include <stdio.h>static int f(int x, int y) { __asm__("shrl $4, %0\n\t""movl %1, %%edx\n\t""addl %%edx, %0" : "+m"(x) // Is this safe without "+&m"? Compilers reject that : "m"(y) : "edx" ); return x;}int main(void) { printf("0x%08X\n", f(0x10000000, 0x10000000));}
Other than using m
constraints instead of r
constraints, it's exactly the same. Now it happens to give the right answer even without the &
. However, I understand relying on this to be a bad idea, since I'm still writing to x
before I read from y
without telling the compiler I'm doing so. But when I change +m
to +&m
, my program no longer compiles: GCC tells me error: input operand constraint contains '&'
, and Clang tells me invalid output constraint '+&m' in asm
. Why doesn't this work?
I can think of two possibilities:
- It's always safe to earlyclobber things in memory, so the
&
is rejected as redundant - It's never safe to earlyclobber things in memory, so the
&
is rejected as unsatisfiable
Is one of those the case? If the latter, what's the best workaround? Or is something else going on here?