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 でこれを行う必要がある場合は、ここで説明されているように、これをチェックする本質的な方法です.