4

Could someone help me understand the assembler given in https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

It goes like this:

uint64_t msr;
asm volatile ( "rdtsc\n\t"    // Returns the time in EDX:EAX.
               "shl $32, %%rdx\n\t"  // Shift the upper bits left.
               "or %%rdx, %0"        // 'Or' in the lower bits.
               : "=a" (msr)
               :
               : "rdx");

How is it different from:

uint64_t msr;
asm volatile ( "rdtsc\n\t"
               : "=a" (msr));

Why do we need shift and or operations and what does rdx at the end do?

EDIT: added what is still unclear to the original question.

  • What does "\n\t" do?
  • What do ":" do?
    • delimiters output/input/clobbers...
  • Is rdx at the end equal to 0?

Just to recap. First line loads the timestamp in registers eax and edx. Second line shifts the value in eax and stores in rdx. Third line ors the value in edx with the value in rdx and saves it in rdx. Fourth line assigns the value in rdx to my variable. The last line sets rdx to 0.

  • Why are the first three lines without ":"?
    • They are a template. First line with ":" is output, second is optional input and third one is optional list of clobbers (changed registers).
  • Is a actually eax and d - edx? Is this hard-coded?

Thanks again! :)

EDIT2: Answered some of my questions...

Leta
  • 331
  • 1
  • 4
  • 16
  • For practical use, see [Get CPU cycle count?](https://stackoverflow.com/q/13772567). (@Mysticial's answer correctly leaves the shift / OR to the compiler, only putting `rdtsc` in the inline asm. My answer suggests the `__rdtsc()` intrinsic instead. Not a duplicate, though, because this question is about learning inline asm using that example, not actually how to implement rdtsc. – Peter Cordes Aug 18 '18 at 14:27
  • Just a remark a few years later, better use `__builtin_ia32_rdtsc()` which is now pervasive across compilers (gcc/clang). – Something Something Sep 07 '22 at 05:23

2 Answers2

8
uint64_t msr;
asm volatile ( "rdtsc\n\t"    // Returns the time in EDX:EAX.
               "shl $32, %%rdx\n\t"  // Shift the upper bits left.
               "or %%rdx, %0"        // 'Or' in the lower bits.
               : "=a" (msr)
               :
               : "rdx");

Because the rdtsc instruction returns it's results in edx and eax, instead of a straight 64-bit register on a 64-bit machine (See the intel system's programming manual for more information; it's an x86 instruction), the 2nd instruction shifts the rdx register to the left 32 bits so that edx will be on the upper 32 bits instead of the lower 32 bits.
"=a" (msr) will move the contents of eax into msr (the %0), i.e. into the lower 32 bits of it, so in total you have edx (higher 32 bits) and eax (lower 32 bits) into rdx which is msr.
rdx is a clobber which will represent the msr C variable.

It's similar to doing the following in C:

static inline uint64_t rdtsc(void)
{
    uint32_t eax, edx;
    asm volatile("rdtsc\n\t", "=a" (eax), "=d" (edx));
    return (uint64_t)eax | (uint64_t)edx << 32;
}

And:

uint64_t msr;
asm volatile ( "rdtsc\n\t"
               : "=a" (msr));

This one, will just give you the contents of eax into msr.

EDIT:

1) "\n\t" is for the generated assembly to look clearer and error-free, so that you don't end up with things like movl $1, %eaxmovl $2, %ebx
2) Is rdx at the end equal to 0? The left shift does this, it removes the bits that are already in rdx.
3) Is a actually eax and d - edx? Is this hard-coded? Yes, there is a table that describes what characters represents what register, e.g. "D" would be rdi, "c" would be ecx, ...

  • I am not quite sure how we work with rdx, and than at the end assign eax to the variable. My logic says: 1) First line loads timestamp in eax and edx. 2) Second line shifts the value in eax left by 32 and stores to rdx. 3) Third line ors the value in edx and rdx and stores into rdx. 4) Fourth line returns eax as output. Where am I wrong??? – Leta Jan 15 '16 at 13:45
  • 2) Second lines shifts `rdx` by 32 bits, and therefore `edx` is now on the upper 32 bits of `rdx` instead of the lower, and then you OR in `eax` in the lower bits of `msr` aka `rdx`, and because of "=a (msr)" you stored `eax` into it at first, hope that makes it clear. –  Jan 15 '16 at 14:03
  • @Leta: Please accept that accepting an acceptable answer is not only acceptable but courteous and some kind of mandatory to be accepted. Thanking someone in the comment section is not sufficient. – zx485 Jan 15 '16 at 15:21
  • Sorry, totally forgot... :) – Leta Jan 15 '16 at 15:23
  • I have updated my answer to make it easier to understand, sorry. –  Jan 15 '16 at 15:40
1

rdtsc returns timestamp in a pair of 32-bit registers (EDX and EAX). First snippet combines them into single 64-bit register (RDX) which is mapped to msr variable.

Second snippet is the wrong one. I'm not sure about what will happen: either it won't be compiled at all, or only part of msr variable will be updated.

gudok
  • 4,029
  • 2
  • 20
  • 30