0

我正在尝试使用需要特定处理器架构的 x86 ASM 功能。我了解调用“ CPUID 标准函数 01H ”后需要检查特定位。下面是来自CPUID Wikipedia页面的用于调用 CPUID的C实现:

#include <stdio.h>

int main() {
    int i;
    unsigned int index = 0;
    unsigned int regs[4];
    int sum;
    __asm__ __volatile__(
#if defined(__x86_64__) || defined(_M_AMD64) || defined (_M_X64)
        "pushq %%rbx     \n\t" /* save %rbx */
#else
        "pushl %%ebx     \n\t" /* save %ebx */
#endif
        "cpuid            \n\t"
        "movl %%ebx ,%[ebx]  \n\t" /* write the result into output var */
#if defined(__x86_64__) || defined(_M_AMD64) || defined (_M_X64)
        "popq %%rbx \n\t"
#else
        "popl %%ebx \n\t"
#endif
        : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
        : "a"(index));
    for (i=4; i<8; i++) {
        printf("%c" ,((char *)regs)[i]);
    }
    for (i=12; i<16; i++) {
        printf("%c" ,((char *)regs)[i]);
    }
    for (i=8; i<12; i++) {
        printf("%c" ,((char *)regs)[i]);
    }
    printf("\n");
}

虽然Linux 内核使用以下函数:

static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
                                unsigned int *ecx, unsigned int *edx)
{
        /* ecx is often an input as well as an output. */
        asm volatile("cpuid"
            : "=a" (*eax),
              "=b" (*ebx),
              "=c" (*ecx),
              "=d" (*edx)
            : "0" (*eax), "2" (*ecx));
}

哪一个更好?其他他们本质上是等价的?

4

1 回答 1

2

正如 Jester 所说,在 GNU C 中,cpuid.h包装器内在函数可能是您最好的选择。


还有__builtin_cpu_supports("popcnt")or"avx"或任何东西,在你调用__builtin_cpu_init(). 但是,仅支持真正的主要功能位。例如,文档没有提到 rdrand 的功能位,所以__builtin_cpu_supports("rdrand")可能不起作用。


自定义版本:

Linux 的实现可以内联而不会浪费指令,而且它看起来写得很好,所以没有理由使用其他任何东西。您可能会收到关于无法满足"=b"约束的投诉;如果是这样,请参阅下面的 clang 的 cpuid.h 的作用。(但我认为这从来没有必要,并且是文档错误的结果)。

volatile但是,如果您将它用于生成的值而不是对管道的序列化效果,它实际上并不需要:使用相同的输入运行 CPUID 将给出相同的结果,因此我们可以让优化器移动它或将其吊出循环。(所以它运行的次数更少)。这可能没有帮助,因为普通代码一开始不会在循环中使用它。


clang 实现的源代码cpuid.h做了一些奇怪的事情,比如保留,%rbx因为显然某些 x86-64 环境可能无法满足%rbx用作输出操作数的约束?评论是/* x86-64 uses %rbx as the base register, so preserve it. */,但我不知道他们在说什么。如果 SysV ABI 中的任何 x86-32 PIC 代码%ebx用于固定目的(作为指向 GOT 的指针),但我不知道 x86-64 的任何类似内容。也许该代码是由 ABI 文档中的错误引起的?请参阅HJ Lu 的邮件列表帖子


最重要的是,问题中的第一个版本(内部main()被破坏了,因为它破坏了红色区域push

要修复它,只需告诉编译器结果将在 ebx(带"=b")中,并让它担心在函数的开始/结束时保存/恢复 ebx/rbx。

于 2016-07-21T09:38:46.767 回答