GCC define muchas macros para determinar en tiempo de compilación si una característica en particular es compatible con la microarquitectura especificada usando -march
. Puede encontrar la lista completa en el código fuente aquí. Está claro que GCC no define tal macro para RDTSCP
(o incluso RDTSC
para esa materia). Los procesadores que soportan RDTSCP
se enumeran en:¿Cuál es el tipo de cpu gcc que incluye soporte para RDTSCP?.
Por lo tanto, puede crear su propia lista (potencialmente incompleta) de microarquitecturas que admitan RDTSCP
. Luego escriba un script de compilación que verifique el argumento pasado a -march
y ver si está en la lista. Si es así, defina una macro como __RDTSCP__
y utilícelo en su código. Supongo que incluso si su lista está incompleta, esto no debería comprometer la corrección de su código.
Desafortunadamente, las hojas de datos de Intel no parecen especificar si un procesador en particular es compatible con RDTSCP
aunque hablan de otras características como AVX2.
Un problema potencial aquí es que no hay garantía de que todos procesador único que implementa una microarquitectura particular, como Skylake, admite RDTSCP
. Sin embargo, no estoy al tanto de tales excepciones.
Relacionado:¿Cuál es el tipo de cpu gcc que incluye soporte para RDTSCP?.
Para determinar la compatibilidad con RDTSCP en tiempo de ejecución , el siguiente código se puede usar en compiladores compatibles con extensiones GNU (GCC, clang, ICC), en cualquier sistema operativo x86. cpuid.h
viene con el compilador, no con el SO.
#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()
ejecuta CPUID dos veces:una vez para verificar el nivel máximo, una vez con el valor de hoja especificado. Devuelve falso si el nivel solicitado ni siquiera está disponible, por eso es parte de un &&
expresión. Probablemente no quiera usar esto cada vez antes de rdtscp, solo como un inicializador para una variable a menos que sea solo un programa único. Véalo en el explorador del compilador Godbolt.
Para MSVC, consulte ¿Cómo detectar la compatibilidad con rdtscp en Visual C++? para código usando su intrínseco.
Para algunas funciones de CPU que GCC conoce, puede usar __builtin_cpu_supports
para verificar un mapa de bits de características que se inicializó temprano en el inicio.
// unfortunately no equivalent for RDTSCP
int sse42_supported() {
return __builtin_cpu_supports("sse4.2");
}
Nota del editor:https://gcc.gnu.org/wiki/DontUseInlineAsm . Esta respuesta durante mucho tiempo no fue segura, y luego se editó para que ni siquiera compilara mientras seguía siendo insegura (golpeando a RAX haciendo que el "a"
restricción insatisfactoria, mientras todavía faltan clobbers en los registros que escribe CPUID). Usa los intrínsecos en otra respuesta. (Pero arreglé el asm en línea en esto para que sea seguro y correcto, en caso de que alguien lo copie/pegue, o quiera aprender a usar restricciones y clobbers correctamente).
Después de investigar un poco más en base a las sugerencias hechas por @Jason, ahora tengo una solución en tiempo de ejecución (todavía no en tiempo de compilación) para determinar si RDTSCP
existe comprobando el bit 28 (ver mapa de bits de salida) del cpuid
instrucción con 0x80000001
como entrada en 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;
}
Si esto no funciona en el código PIC de 32 bits debido al bloqueo de EBX, entonces 1. deje de usar PIC de 32 bits porque es ineficiente en comparación con PIC de 64 bits o en comparación con -fno-pie -no-pie
ejecutables. 2. obtenga un GCC más nuevo que permita clobbers de EBX incluso en código PIC de 32 bits, emitiendo instrucciones adicionales para guardar/restaurar EBX o lo que sea necesario. 3. use la versión intrínseca (que debería funcionar para usted).
Por ahora estoy bien con los compiladores GNU, pero si alguien necesita hacer esto bajo MSVC, entonces es una forma intrínseca de verificar esto como se explica aquí.