汇编指令:rdtsc

4
有人能帮我理解在 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html 中给出的汇编代码吗?
代码如下:
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");

与之不同的是:

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

我们为什么需要使用移位和或操作,rdx在末尾是做什么用的?
编辑:原问题中还不清楚的内容已添加。
- "\n\t"是什么作用? - ":"是什么作用?
- 分隔符输出/输入/破坏...
- rdx在末尾是否等于0? - 简要回顾一下。第一行将时间戳加载到eax和edx寄存器中。第二行移位eax中的值,并将其存储在rdx中。第三行将edx中的值与rdx中的值进行或运算,并将其保存在rdx中。第四行将rdx中的值分配给我的变量。最后一行将rdx设置为0。 - 为什么前三行没有":"?
- 它们是模板。带有":"的第一行是输出,第二行是可选输入,第三行是可选的更改寄存器列表。
- a实际上是eax,d是edx吗?这是硬编码的吗?
谢谢! :)
编辑2:回答了我一些问题...

有关实际用途,请参见获取CPU周期计数?。(@Mysticial的答案正确地将移位/OR留给编译器,只将rdtsc放在内联汇编中。我的答案建议使用__rdtsc()内置函数。虽然不是重复问题,但这个问题是关于使用该示例学习内联汇编,而不是实际如何实现rdtsc。 - Peter Cordes
几年后的现在,只是一个小建议,最好使用__builtin_ia32_rdtsc(),这个函数现在已经被广泛地支持于各种编译器(如gcc/clang)中。 - Henrique Bucher
2个回答

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");

因为rdtsc指令在64位机器上将其结果返回到edxeax而不是一个简单的64位寄存器(请参阅英特尔系统编程手册以获取更多信息;它是x86指令),所以第二条指令将rdx寄存器向左移动32位,使得edx将在高32位而不是低32位。
"=a"(msr)将会把eax的内容移动到msr(即%0)中,也就是说总共有edx(高32位)和eax(低32位)进入rdx,其中rdxmsr
rdx是一个clobber,将表示msr的C变量。

这类似于在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;
}

并且:

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

这个指令将eax的内容直接存储到msr中。

编辑:

1)"\n\t"是为了让生成的汇编代码更清晰、无误,以避免出现类似于movl $1, %eaxmovl $2, %ebx这样的情况。
2)最终的rdx是否为0?左移操作可以实现此功能,可以移除rdx中已经存在的位。
3)a是否真的代表eax,d是否代表edx?这是硬编码吗? 是的,有一张表格描述了哪个字符表示哪个寄存器,例如,“D”表示rdi,“c”表示ecx,...


我不太确定我们如何使用rdx,然后最后将eax赋值给变量。我的逻辑是: 1)第一行将时间戳加载到eax和edx中。 2)第二行将eax中的值左移32位并存储到rdx中。 3)第三行将edx和rdx中的值进行或运算,并存储到rdx中。 4)第四行将eax作为输出返回。我错在哪里? - Leta
第二行将 rdx 向左移动了32位,因此 edx 现在在 rdx 的高32位而不是低32位上,然后您在 msrrdx 的低位中进行 OR 操作,并且由于 "=a(msr)",您首先将 eax 存储到其中,希望这样清晰明了。 - user1551592
@Leta:请接受一个可接受的答案不仅是可以接受的,而且是礼貌和某种必须被接受的方式。在评论区感谢某人是不够的。 - zx485
抱歉,完全忘记了... :) - Leta
我已经更新了我的答案,使其更易于理解,抱歉。 - user1551592

1

rdtsc返回一对32位寄存器(EDX和EAX)中的时间戳。第一个代码片段将它们合并成单个64位寄存器(RDX),该寄存器映射到msr变量。

第二个代码片段是错误的。我不确定会发生什么:要么根本无法编译,要么只会更新msr变量的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接