0

特定のプロセッサ アーキテクチャを必要とする x86 ASM 機能を利用しようとしています。「 CPUID 標準関数 01H 」を呼び出した後、特定のビットを確認する必要があることは理解しています。以下は、 CPUIDを呼び出すためのCPUID Wikipediaページからの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")を呼び出した後に機能するまたは"avx"何でもあります__builtin_cpu_init()。ただし、本当に主要な機能ビットのみがサポートされています。たとえば、ドキュメントではrdrandの機能ビットについて言及していないため、__builtin_cpu_supports("rdrand")おそらく機能しません。


カスタムバージョン:

Linux からの実装は、無駄な命令なしでインライン化でき、よく書かれているように見えるので、他のものを使用する理由はありません。"=b"制約を満たすことができないという苦情を受ける可能性はほとんどありません。その場合、clang の cpuid.h の機能については以下を参照してください。(しかし、それは決して必要ではなく、ドキュメントの間違いの結果だと思います)。

volatileただし、パイプラインでのシリアル化効果ではなく生成される値に使用する場合は、実際には必要ありません。同じ入力で CPUID を実行しても同じ結果が得られるため、オプティマイザに移動させたり、ループから持ち上げます。(したがって、実行回数が少なくなります)。ただし、通常のコードでは最初からループで使用しないため、これはおそらく役に立ちません。


cpuid.h明らかに一部の x86-64 環境では、出力オペランドとして%rbx使用する制約を満たすことができない可能性があるため、保持するなど、clang の実装のソースはいくつかの奇妙なことを行いますか? %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 に答える