GCC definisce molte macro per determinare in fase di compilazione se una particolare funzionalità è supportata dalla microarchitettura specificata usando -march
. Puoi trovare l'elenco completo nel codice sorgente qui. È chiaro che GCC non definisce tale macro per RDTSCP
(o anche RDTSC
per questo motivo). I processori che supportano RDTSCP
sono elencati in:Qual è il tipo di cpu di gcc che include il supporto per RDTSCP?.
Quindi puoi creare il tuo elenco (potenzialmente incompleto) di microarchitetture che supportano RDTSCP
. Quindi scrivi uno script di compilazione che controlla l'argomento passato a -march
e vedere se è nella lista. Se lo è, definisci una macro come __RDTSCP__
e usalo nel tuo codice. Presumo che anche se la tua lista è incompleta, ciò non dovrebbe compromettere la correttezza del tuo codice.
Sfortunatamente, i fogli dati di Intel non sembrano specificare se un particolare processore supporta RDTSCP
anche se discutono di altre funzionalità come AVX2.
Un potenziale problema qui è che non vi è alcuna garanzia che ogni singolo processore che implementa una particolare microarchitettura, come Skylake, supporta RDTSCP
. Tuttavia non sono a conoscenza di tali eccezioni.
Correlati:Qual è il tipo di cpu gcc che include il supporto per RDTSCP?.
Per determinare il supporto RDTSCP in fase di esecuzione , il seguente codice può essere utilizzato su compilatori che supportano le estensioni GNU (GCC, clang, ICC), su qualsiasi sistema operativo x86. cpuid.h
viene fornito con il compilatore, non con il sistema operativo.
#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()
esegue CPUID due volte:una volta per controllare il livello massimo, una volta con il valore foglia specificato. Restituisce false se il livello richiesto non è nemmeno disponibile, ecco perché fa parte di un &&
espressione. Probabilmente non vuoi usarlo ogni volta prima di rdtscp, proprio come inizializzatore per una variabile a meno che non sia solo un semplice programma una tantum. Guardalo sull'esploratore del compilatore Godbolt.
Per MSVC, vedere Come rilevare il supporto rdtscp in Visual C++? per il codice utilizzando il suo intrinseco.
Per alcune funzionalità della CPU che GCC conosce, puoi usare __builtin_cpu_supports
per controllare una bitmap di funzionalità inizializzata all'inizio dell'avvio.
// unfortunately no equivalent for RDTSCP
int sse42_supported() {
return __builtin_cpu_supports("sse4.2");
}
Nota dell'editore:https://gcc.gnu.org/wiki/DontUseInlineAsm . Questa risposta per molto tempo non è stata sicura e in seguito è stata modificata per non essere nemmeno compilata pur essendo ancora non sicura (bloccando RAX creando il "a"
vincolo insoddisfacente, mentre mancano ancora i clobber sui registri scritti da CPUID). Usa gli intrinseci in un'altra risposta. (Ma ho corretto l'asm in linea in questo per essere sicuro e corretto, nel caso qualcuno lo copi/incolli o voglia imparare come usare correttamente vincoli e clobber.)
Dopo aver indagato un po' di più sulla base dei suggerimenti forniti da @Jason, ora ho una soluzione in fase di esecuzione (ancora non in fase di compilazione) per determinare se RDTSCP
esiste controllando il 28esimo bit (vedi output bitmap) del cpuid
istruzione con 0x80000001
come input in 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;
}
Se questo non funziona nel codice PIC a 32 bit a causa del blocco EBX, allora 1. smetti di usare PIC a 32 bit perché è inefficiente rispetto a PIC a 64 bit o rispetto a -fno-pie -no-pie
eseguibili. 2. ottenere un GCC più recente che consenta il clobber di EBX anche nel codice PIC a 32 bit, emettendo istruzioni aggiuntive per salvare/ripristinare EBX o qualsiasi altra cosa sia necessaria. 3. usa la versione intrinseca (che dovrebbe funzionare per te).
Per ora sto bene con i compilatori GNU, ma se qualcuno ha bisogno di farlo sotto MSVC, allora è un modo intrinseco per verificarlo come spiegato qui.