与高级语言不同的CPUID使用情况

时间:2016-07-20 15:24:32

标签: c linux assembly cpuid

我试图利用需要某种处理器架构的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 kernel使用以下功能:

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

哪一个更好?其他他们同样相当吗?

1 个答案:

答案 0 :(得分:2)

正如Jester所说,在GNU C中,cpuid.h封装内在可能是你最好的选择。

还有__builtin_cpu_supports("popcnt")"avx"或其他任何内容,只有在您致电__builtin_cpu_init()后才有效。但是,只支持真正的主要功能位。例如,文档没有提及rdrand的功能位,因此__builtin_cpu_supports("rdrand")可能无法正常工作。

自定义版本:

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

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

The source for clang's implementation of 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's mailing list post about it

最重要的是,问题中的第一个版本(内部main()已被破坏,因为它clobbers the red-zone with push

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