Minimal educational example
Here I provide a minimal educational example that attempts to make what https://stackoverflow.com/a/15819941/895245 mentioned clearer.
This specific code is of course not useful in practice, and could be achieved more efficiently a single lea 1(%q[in]), %out
instruction, it is just a simple educational example.
main.c
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint64_t in = 1;
uint64_t out;
__asm__ (
"mov %[in], %[out];" /* out = in */
"inc %[out];" /* out++ */
"mov %[in], %[out];" /* out = in */
"inc %[out];" /* out++ */
: [out] "=&r" (out)
: [in] "r" (in)
:
);
assert(out == 2);
}
Compile and run:
gcc -ggdb3 -std=c99 -O3 -Wall -Wextra -pedantic -o main.out main.c
./main.out
This program is correct and the assert passes, because &
forces the compiler to choose different registers for in
and out
.
This is because &
tells the compiler that in
might be used after out
was written to, which is actually the case here.
Therefore, the only way to not wrongly modify in
is to put in
and out
in different registers.
The disassembly:
gdb -nh -batch -ex 'disassemble/rs main' main.out
contains:
0x0000000000001055 <+5>: 48 89 d0 mov %rdx,%rax
0x0000000000001058 <+8>: 48 ff c0 inc %rax
0x000000000000105b <+11>: 48 89 d0 mov %rdx,%rax
0x000000000000105e <+14>: 48 ff c0 inc %rax
which shows that GCC chose rax
for out
and rdx
for in
.
If we remove the &
however, the behavior is unspecified.
In my test system, the assert actually fails, because the compiler tries to minimize register usage, and compiles to:
0x0000000000001055 <+5>: 48 89 c0 mov %rax,%rax
0x0000000000001058 <+8>: 48 ff c0 inc %rax
0x000000000000105b <+11>: 48 89 c0 mov %rax,%rax
0x000000000000105e <+14>: 48 ff c0 inc %rax
therefore using rax
for both in
and out
.
The result of this is that out
is incremented twice, and equals 3
instead of 2
in the end.
Tested in Ubuntu 18.10 amd64, GCC 8.2.0.
More practical examples