1

I am trying to use the following inline assembly in C to read the high word (%edx) of Time Stamp Counter for time measurement:

unsigned int tsc;
asm volatile ("rdtscp; xchgl %%edx, %%eax" : "=r" (tsc));

Unfortunately, the code crashes. If the xchgl instruction is removed, or use rdtsc instruction, there is no problem. For the former, although the code does not crash, I have no way to take out what I want -- the value in %edx register. I have checked an online document about inline assembly in C but failed to find any clue to return the value in %edx to the output C variable directly without any additional instructions (if I change xchgl to movl, the same crash occurs). With little hope that I missed any syntax or did not understand the document correctly, I come here to ask: is there any way to specify %edx register to be the output instead of conventional %eax in inline assembly in C? Thank you.

PS1: I am working in Linux on an intel i386 compatible CPU whose TSC works well to me.

PS2: For some reason I just need the value in %edx.

zzzhhh
  • 319
  • 1
  • 8
  • The reason it crashes is that it doesn’t tell the compiler that the assembly code changes eax and edx. – prl Apr 11 '21 at 18:23
  • 1
    There is no “conventional” output register for inline assembly. The compiler chooses whatever is most convenient unless you specify a register. – prl Apr 11 '21 at 18:27

1 Answers1

2

You need to specify the register as a constraint to tell the compiler that it has to pick a specific register.

unsigned int tsc;
asm volatile ("rdtsc" : "=d" (tsc) : : "eax");

"d" is the edx register as the output operand. "a" is the eax register in the clobber list because its content is altered by the instruction. There are two colons in between because there are no input operands.

Your "=r" lets the compiler pick any register; it only happens to pick EAX in a debug build in a function that doesn't inline. You also need to tell the compiler about all other registers that are modified, even if you don't want them as outputs; the compiler assumes all registers and memory are unmodified and unread unless you tell it otherwise. https://stackoverflow.com/tags/inline-assembly/info


Normally you'd use intrinsics instead of inline asm for portability and to avoid messing with asm:
How to get the CPU cycle count in x86_64 from C++?

But if you did want safe inline asm to get both halves, on both 32 bit and 64 bit:

unsigned int tsc_hi, tsc_lo;
asm volatile ("rdtsc" : "=a" (tsc_lo), "=d" (tsc_hi));

Alternatively, you can use 'A' constraint for the value in the edx:eax register-pair on a 32 bit machine. But in a 64-bit built, "A" lets the compiler pick either RDX or RAX for the 64-bit integer; it would only be both with an unsigned __int128.

// Beware: breaks silently if compiled as 64-bit code
unsigned long long tsc;
asm volatile ("rdtsc" : "=A" (tsc));
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Emanuel P
  • 1,586
  • 1
  • 6
  • 15
  • @prl Yes, that is correct. I've edited the post to reflect that. – Emanuel P Apr 11 '21 at 18:46
  • 2
    You normally don't need or want inline asm for RDTSC; [How to get the CPU cycle count in x86\_64 from C++?](https://stackoverflow.com/a/51907627) shows the portable way with intrinsics. (Also, you're right that `"=A"` will work in 32-bit code, but I'd highly discourage it because it breaks silently in 64-bit code, where an "A" constraint lets the compiler pick *either* RAX or RDX for a 64-bit integer. That's what the linked RDTSC Q&A was originally about.) But yeah, your first way is good for discarding the low-half result. – Peter Cordes Apr 11 '21 at 20:48