ElBlo
Inline Assembly to force a read from a pointer
How many times have you been surprised by a compiler optimization removing your dead reads? My coworker Roland told me of a neat and portable way of making sure the compiler won’t optimize a way a memory read, without writing any assembly code.
To read a value from a pointer, you can do:
__asm__ volatile("" : "=r" (x) : "0"(*p));
This is equivalent to x = *p;
Explanation:
"=r"
is the constraint on the output operand (operand number 0), saying that it must be a register."0"
is the constraint on the input operand (operand number 1). It is a matching constraint that says that it must be the same operand as operand number 0.
These constrains tell the compiler that before executing any assembly code, it
must provide a register %0
that has the contents of *p
in it.
Note that this might not work if p
can be the NULL pointer, as the compiler could assume it is undefined behavior. To do that, you can “launder” the pointer beforehand:
__asm__("" : "=r"(p) : "0"(p));
Here is some sample code and the corresponding generated code:
int force_read(int* p) {
int x = 3;
__asm__ volatile("" : "=r" (x) : "0"(*p));
return x;
}
int force_read_null() {
int* p = NULL;
int x = 3;
__asm__("" : "=r"(p) : "0"(p));
__asm__ volatile("" : "=r" (x) : "0"(*p));
return x;
}
force_read:
mov eax, DWORD PTR [rdi]
ret
force_read_null:
xor eax, eax
mov eax, DWORD PTR [rax]
ret