I just learned clobber usage through these docs(https://gcc.gnu.org/onlinedocs/gcc-7.5.0/gcc/Extended-Asm.html) as well as previous questions.
I also read another introduction post. The description in the post is:
Its purpose is to inform the compiler that the registers in the Clobbers list will be implicitly modified by the assembly code in the asm statement. Since the registers in Clobbers will be implicitly modified by the assembly code in the asm statement, the compiler will not use the registers specified in Clobbers when selecting registers for input operands and output operands, thus avoiding logical errors such as data overwriting.
In actual use, I still have some confusion.
- If I specify the registers used by input and output, do I still need to write clobber? I saw an example introducing clobber.
#include <stdio.h>
int inc2(int src) {
int dst;
asm("mov %1, %0\n\t"
"mov $3, %%eax\n\t"
"add $1, %0"
: "=r"(dst)
: "r"(src));
return dst;
}
int inc3(int src) {
int dst;
asm("mov %1, %0\n\t"
"mov $3, %%eax\n\t"
"add $1, %0"
: "=r"(dst)
: "r"(src)
: "%eax");
return dst;
}
int main(int argc, char *argv[]) {
printf("inc2: %d\n", inc2(1));
printf("inc3: %d\n", inc3(1));
}
The above example outputs are 2 and 4 respectively. The reason is that the eax register is used in mov $3, %eax
in the src2
function, and eax
is also selected as the input register.
If clobber is used to indicate which registers should not be selected as input registers, if I explicitly instruct, is there no need to write clobber? For example int inc4(int src) { int dst;
asm("mov %%rcx, %%rax\n\t"
"add $1, %%rax"
: "=a"(dst)
: "c"(src)
: );
return dst;
}
In the above example, the input register is explicitly ecx and the output is explicitly eax. Is it possible that by this method I can avoid writing clobber altogether? Or should the registers modified in assembly be marked?
- I have seen some use of inline assembly in some open source code, but it is not consistent with my understanding. But these codes are written by very professional people and widely used by many people. I think it's very precise. I would like to analyze some of how to write inline assembly canonically through these examples.
(1) Why does the two functions below clobber include edx and ecx? Is it because the output register (=a) selects eax, so the clobber does not need to be full of eax? There are no input parameters in this function, only one output. What is the significance of marking these two registers?
static inline uint32_t rdtscp() {
uint32_t rv;
asm volatile ("rdtscp": "=a" (rv) :: "edx", "ecx");
return rv;
}
static inline uint32_t memaccesstime(void *v) {
uint32_t rv;
asm volatile (
"mfence\n"
"lfence\n"
"rdtscp\n"
"mov %%eax, %%esi\n"
"mov (%1), %%eax\n"
"rdtscp\n"
"sub %%esi, %%eax\n"
: "=&a" (rv): "r" (v): "ecx", "edx", "esi");
return rv;
}
Thanks!