GCC mendefinisikan banyak makro untuk menentukan pada waktu kompilasi apakah fitur tertentu didukung oleh mikroarsitektur yang ditentukan menggunakan -march
. Anda dapat menemukan daftar lengkapnya di kode sumber di sini. Jelas bahwa GCC tidak mendefinisikan makro seperti itu untuk RDTSCP
(atau bahkan RDTSC
untuk masalah itu). Prosesor yang mendukung RDTSCP
tercantum di:Apa jenis cpu gcc yang menyertakan dukungan untuk RDTSCP?.
Jadi, Anda dapat membuat mikroarsitektur daftar sendiri (mungkin tidak lengkap) yang mendukung RDTSCP
. Kemudian tulis skrip build yang memeriksa argumen yang diteruskan ke -march
dan lihat apakah itu ada dalam daftar. Jika ya, tentukan makro seperti __RDTSCP__
dan menggunakannya dalam kode Anda. Saya berasumsi bahwa meskipun daftar Anda tidak lengkap, hal ini tidak boleh mengganggu kebenaran kode Anda.
Sayangnya, lembar data Intel tampaknya tidak menentukan apakah prosesor tertentu mendukung RDTSCP
meskipun mereka membahas fitur lain seperti AVX2.
Salah satu potensi masalah di sini adalah bahwa tidak ada jaminan bahwa setiap prosesor tunggal yang mengimplementasikan mikroarsitektur tertentu, seperti Skylake, mendukung RDTSCP
. Saya tidak mengetahui pengecualian seperti itu.
Terkait:Apa jenis cpu gcc yang menyertakan dukungan untuk RDTSCP?.
Untuk menentukan dukungan RDTSCP saat run-time , kode berikut dapat digunakan pada kompiler yang mendukung ekstensi GNU (GCC, dentang, ICC), pada OS x86 apa pun. cpuid.h
disertakan dengan kompiler, bukan 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()
menjalankan CPUID dua kali:sekali untuk memeriksa level maksimal, sekali dengan nilai daun yang ditentukan. Mengembalikan false jika level yang diminta bahkan tidak tersedia, itu sebabnya ini adalah bagian dari &&
ekspresi. Anda mungkin tidak ingin menggunakan ini setiap kali sebelum rdtscp, hanya sebagai penginisialisasi variabel kecuali itu hanya program satu kali yang sederhana. Lihat di penjelajah kompiler Godbolt.
Untuk MSVC, lihat Bagaimana cara mendeteksi dukungan rdtscp di Visual C++? untuk kode menggunakan intrinsiknya.
Untuk beberapa fitur CPU yang diketahui GCC, Anda dapat menggunakan __builtin_cpu_supports
untuk memeriksa bitmap fitur yang diinisialisasi di awal startup.
// unfortunately no equivalent for RDTSCP
int sse42_supported() {
return __builtin_cpu_supports("sse4.2");
}
Catatan editor:https://gcc.gnu.org/wiki/DontUseInlineAsm . Jawaban ini untuk waktu yang lama tidak aman, dan kemudian diedit bahkan tidak dapat dikompilasi saat masih tidak aman (memukul RAX membuat "a"
kendala tidak dapat dipenuhi, sementara masih ada clobbers yang hilang pada register yang ditulis CPUID). Gunakan intrinsik dalam jawaban lain. (Tapi saya telah memperbaiki inline asm agar aman dan benar, jika ada yang menyalin/menempelnya, atau ingin mempelajari cara menggunakan batasan dan clobbers dengan benar.)
Setelah menyelidiki lebih banyak berdasarkan saran yang dibuat oleh @Jason, saya sekarang memiliki solusi run-time (masih bukan waktu kompilasi) untuk menentukan apakah RDTSCP
ada dengan memeriksa bit ke-28 (lihat output bitmap) dari cpuid
instruksi dengan 0x80000001
sebagai input di 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;
}
Jika ini tidak berhasil dalam kode PIC 32-bit karena clobber EBX, maka 1. berhenti menggunakan PIC 32-bit karena tidak efisien vs. PIC 64-bit atau vs. -fno-pie -no-pie
dapat dieksekusi. 2. dapatkan GCC yang lebih baru yang memungkinkan penghancuran EBX bahkan dalam kode PIC 32-bit, memancarkan instruksi tambahan untuk menyimpan/mengembalikan EBX atau apa pun yang diperlukan. 3. gunakan versi intrinsik (yang seharusnya mengatasi hal ini untuk Anda).
Untuk saat ini saya baik-baik saja dengan kompiler GNU, tetapi jika seseorang perlu melakukan ini di bawah MSVC, maka cara intrinsik untuk memeriksanya seperti yang dijelaskan di sini.