1

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.

  1. 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?

  1. 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!

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Gerrie
  • 736
  • 3
  • 18
  • Any register you modify needs to be an output operand *or* declared as a clobber. Otherwise it's Undefined Behaviour. e.g. for `rdtscp`, you're telling the compiler about the low half of the EDX:EAX TSC result as an output operand, and telling it that the other two registers the instruction writes are clobbered. – Peter Cordes Jun 29 '22 at 08:46
  • Thanks! But why should ecx be included? Doesn't seem to use ecx? – Gerrie Jun 29 '22 at 09:00
  • 1
    Are you maybe missing the difference between `rdtscP` (which also writes a per-core ID # to ECX - https://www.felixcloutier.com/x86/rdtscp) vs. the older `rdtsc` (which only writes the TSC to EDX:EAX - https://www.felixcloutier.com/x86/rdtsc) – Peter Cordes Jun 29 '22 at 09:03

0 Answers0