如何在 C 中的内联汇编中将 %edx 指定为输出而不是传统的 %eax?

问题描述

我正在尝试在 C 中使用以下内联汇编来读取时间戳计数器的高位字 (%edx) 以进行时间测量:

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

不幸的是,代码崩溃了。如果去掉xchgl指令,或者使用rdtsc指令,都没有问题。对于前者,虽然代码没有崩溃,但是我没有办法取出我想要的——%edx寄存器中的值。我已经检查了有关 C 中内联汇编的在线 document,但没有找到任何线索将 %edx 中的值直接返回到输出 C 变量而无需任何其他说明(如果我更改 xchglmovl,发生同样的崩溃)。几乎没有希望我错过了任何语法或没有正确理解文档,我来到这里问:有没有办法指定 %edx 寄存器作为输出,而不是内联汇编中的常规 %eax C?谢谢。

PS1:我在 Linux 上使用英特尔 i386 兼容 cpu,其 TSC 对我来说效果很好。

PS2:出于某种原因,我只需要 %edx 中的值。

解决方法

您需要将寄存器指定为约束,以告诉编译器它必须选择一个特定的寄存器。

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

"d" 是作为输出操作数的 edx 寄存器。 "a" 是 clobber 列表中的 eax 寄存器,因为它的内容被指令改变了。中间有两个冒号,因为没有输入操作数。

你的“=r”让编译器选择任何寄存器;它只会在不内联的函数中的调试版本中选择 EAX。您还需要告诉编译器所有其他被修改的寄存器,即使您不希望它们作为输出;除非您另有说明,否则编译器假定所有寄存器和内存都未修改且未读取。 https://stackoverflow.com/tags/inline-assembly/info


通常您会使用内在函数 instead of inline asm 来实现可移植性并避免与 asm 混淆:
How to get the CPU cycle count in x86_64 from C++?

但是,如果您确实希望安全的内联汇编在 32 位和 64 位上得到两半:

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

或者,您可以在 32 位机器上对 edx:eax 寄存器对中的值使用“A”约束。但是在 64 位构建中,“A”让编译器为 64 位整数选择 RDX 或 RAX;它只会同时带有 unsigned __int128

// Beware: breaks silently if compiled as 64-bit code
unsigned long long tsc;
asm volatile ("rdtsc" : "=A" (tsc));