GNU/Linux >> Linux の 問題 >  >> Linux

コンパイル時にプロセッサに RDTSCP があるかどうかを検出する

GCC は、特定の機能が -march を使用して指定されたマイクロアーキテクチャによってサポートされているかどうかをコンパイル時に判断するために、多くのマクロを定義しています。 .完全なリストは、こちらのソース コードにあります。 GCC が RDTSCP に対してそのようなマクロを定義していないことは明らかです (または RDTSC そのことについては)。 RDTSCP をサポートするプロセッサ RDTSCP のサポートを含む gcc cpu-type とは?.

したがって、RDTSCP をサポートする独自の (不完全な可能性がある) リスト マイクロアーキテクチャを作成できます。 .次に、-march に渡された引数をチェックするビルド スクリプトを記述します。 リストにあるかどうかを確認します。そうであれば、__RDTSCP__ などのマクロを定義します。 コードで使用します。リストが不完全であっても、コードの正確性が損なわれることはないと思います.

残念ながら、インテルのデータシートには、特定のプロセッサーが RDTSCP をサポートしているかどうかが明記されていないようです。 AVX2 などの他の機能についても説明していますが。

ここで考えられる問題の 1 つは、すべて Skylake などの特定のマイクロアーキテクチャを実装するシングル プロセッサは、RDTSCP をサポートします。 .ただし、そのような例外については知りません。

関連:RDTSCP のサポートを含む gcc cpu-type とは?

実行時に RDTSCP サポートを確認するには 、次のコードは、任意の x86 OS 上の GNU 拡張 (GCC、clang、ICC) をサポートするコンパイラで使用できます。 cpuid.h OS ではなく、コンパイラに付属しています。

#include <cpuid.h>

int rdtscp_supported(void) {
    unsigned a, b, c, d;
    if (__get_cpuid(0x80000001, &a, &b, &c, &d) && (d & (1<<27)))
    {
        // RDTSCP is supported.
        return 1;
    }
    else
    {
        // RDTSCP is not supported.
        return 0;
    }
}

__get_cpuid() CPUID を 2 回実行します。1 回は最大レベルをチェックするため、もう 1 回は指定されたリーフ値を使用します。要求されたレベルが利用できない場合は false を返します。これが && の一部である理由です。 表現。単純な 1 回限りのプログラムでない限り、これを変数の初期化子として、rdtscp の前に毎回使用したくないでしょう。 Godbolt コンパイラ エクスプローラで参照してください。

MSVC については、Visual C++ で rdtscp サポートを検出する方法を参照してください。組み込みを使用するコードの場合。

GCC が認識している一部の CPU 機能については、__builtin_cpu_supports を使用できます。 起動初期に初期化された機能ビットマップをチェックします。

// unfortunately no equivalent for RDTSCP
int sse42_supported() {
    return __builtin_cpu_supports("sse4.2");
}

編集者注:https://gcc.gnu.org/wiki/DontUseInlineAsm .この回答は長い間安全ではありませんでしたが、後で編集して、安全でないままコンパイルさえしないようにしました (RAX を壊して "a" を作成します)。 CPUID が書き込むレジスターのクロバーがまだ欠落している間、制約は満たされない)。別の回答で組み込み関数を使用します。 (しかし、誰かがそれをコピー/貼り付けしたり、制約とクロバーを適切に使用する方法を学びたい場合に備えて、これでインライン asm を安全かつ正確になるように修正しました。)

@Jason の提案に基づいてもう少し調査した後、RDTSCP cpuid の 28 番目のビット (出力ビットマップを参照) をチェックすることによって存在します 0x80000001 による命令 EAX の入力として .

int if_rdtscp() {
    unsigned int edx;
    unsigned int eax = 0x80000001;
#ifdef __GNUC__              // GNU extended asm supported
    __asm__ (     // doesn't need to be volatile: same EAX input -> same outputs
     "CPUID\n\t"
    : "+a" (eax),         // CPUID writes EAX, but we can't declare a clobber on an input-only operand.
      "=d" (edx)
    : // no read-only inputs
    : "ecx", "ebx");      // CPUID writes E[ABCD]X, declare clobbers

    // a clobber on ECX covers the whole RCX, so this code is safe in 64-bit mode but is portable to either.

#else // Non-gcc/g++ compilers.
    // To-do when needed
#endif
    return (edx >> 27) & 0x1;
}

これが 32 ビット PIC コードで EBX クロバーのために機能しない場合は、1. 64 ビット PIC または -fno-pie -no-pie に対して効率が悪いため、32 ビット PIC の使用をやめます。 実行可能ファイル。 2. 32 ビットの PIC コードでも EBX クロバーを許可する新しい GCC を取得し、EBX を保存/復元するための追加の命令を発行するか、必要なものを何でも発行します。 3. 組み込みバージョンを使用します (これで回避できます)。

今のところ、私は GNU コンパイラで問題ありませんが、誰かが MSVC でこれを行う必要がある場合は、ここで説明されているように、これをチェックする本質的な方法です.


Linux
  1. ファイルがダウンロードされたことを検出することは可能ですか?

  2. バッシュを検出する方法>=4.0?

  3. ソースツリーにマウントポイントがある場合にファイルのタイムスタンプを保持するようにRsyncに指示するにはどうすればよいですか?

  1. Linux プロセスの状態

  2. C での 64 ビット コンパイルの検出

  3. BashでファイルにUTF-8 BOMがあるかどうかを検出する方法は?

  1. Linuxでゲームをプレイするのにこれほど良い時期はありませんでした

  2. BIOS で VT-X がオンになっているかどうかを検出する方法は?

  3. 複数のコマンドの実行時間を計る