diff --git a/include/winnt.h b/include/winnt.h index f2df02ca48..668d3811bc 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -518,6 +518,9 @@ typedef struct _SINGLE_LIST_ENTRY { #define PF_MMX_INSTRUCTIONS_AVAILABLE 3 #define PF_PPC_MOVEMEM_64BIT_OK 4 #define PF_ALPHA_BYTE_INSTRUCTIONS 5 +#define PF_XMMI_INSTRUCTIONS_AVAILABLE 6 +#define PF_AMD3D_INSTRUCTIONS_AVAILABLE 7 +#define PF_RDTSC_INSTRUCTION_AVAILABLE 8 /* The Win32 register context */ diff --git a/misc/cpu.c b/misc/cpu.c index 44fb403058..6063a2c8b7 100644 --- a/misc/cpu.c +++ b/misc/cpu.c @@ -198,6 +198,8 @@ VOID WINAPI GetSystemInfo( PF[PF_COMPARE_EXCHANGE_DOUBLE] = TRUE; if (strstr(value,"mmx")) PF[PF_MMX_INSTRUCTIONS_AVAILABLE] = TRUE; + if (strstr(value,"tsc")) + PF[PF_RDTSC_INSTRUCTION_AVAILABLE] = TRUE; } } diff --git a/win32/newfns.c b/win32/newfns.c index ca02763e8a..3af7cb39ae 100644 --- a/win32/newfns.c +++ b/win32/newfns.c @@ -7,6 +7,7 @@ /* Misc. new functions - they should be moved into appropriate files at a later date. */ +#include #include #include #include @@ -19,6 +20,69 @@ DEFAULT_DEBUG_CHANNEL(win32); DECLARE_DEBUG_CHANNEL(debug); +static BOOL QUERYPERF_Initialized = 0; +#if defined(__i386__) && defined(__GNUC__) +static BOOL QUERYPERF_RDTSC_Use = 0; +static LONGLONG QUERYPERF_RDTSC_Frequency = 0; +#endif + +static void QUERYPERF_Init(void) +{ +#if defined(__i386__) && defined(__GNUC__) + /* We are running on i386 and compiling on GCC. + * Do a runtime check to see if we have the rdtsc instruction available + */ + FILE *fp; + char line[256], *s, *value; + double cpuMHz; + + TRACE("()\n"); + + if (IsProcessorFeaturePresent( PF_RDTSC_INSTRUCTION_AVAILABLE )) + { + /* rdtsc is available. However, in order to use it + * we also need to be able to get the processor's + * speed. Currently we do this by reading /proc/cpuinfo + * which makes it Linux-specific. + */ + + TRACE("rdtsc available\n"); + + fp = fopen( "/proc/cpuinfo", "r" ); + if (fp) + { + while(fgets( line, sizeof(line), fp )) + { + /* NOTE: the ':' is the only character we can rely on */ + if (!(value = strchr( line, ':' ))) + continue; + + /* terminate the valuename */ + *value++ = '\0'; + /* skip any leading spaces */ + while (*value == ' ') value++; + if ((s = strchr( value, '\n' ))) + *s = '\0'; + + if (!strncasecmp( line, "cpu MHz", strlen( "cpu MHz" ) )) + { + if (sscanf( value, "%lf", &cpuMHz ) == 1) + { + QUERYPERF_RDTSC_Frequency = (LONGLONG)(cpuMHz * 1000000.0); + QUERYPERF_RDTSC_Use = TRUE; + TRACE("using frequency: %lldHz\n", QUERYPERF_RDTSC_Frequency); + break; + } + } + } + fclose(fp); + } + } +#endif + QUERYPERF_Initialized = TRUE; +} + + /**************************************************************************** * QueryPerformanceCounter (KERNEL32.564) */ @@ -26,9 +90,23 @@ BOOL WINAPI QueryPerformanceCounter(PLARGE_INTEGER counter) { struct timeval tv; - gettimeofday(&tv,NULL); - counter->s.LowPart = tv.tv_usec+tv.tv_sec*1000000; - counter->s.HighPart = 0; + if (!QUERYPERF_Initialized) + QUERYPERF_Init(); + +#if defined(__i386__) && defined(__GNUC__) + if (QUERYPERF_RDTSC_Use) + { + /* i586 optimized version */ + __asm__ __volatile__ ( "rdtsc" + : "=a" (counter->s.LowPart), "=d" (counter->s.HighPart) ); + return TRUE; + } + /* fall back to generic routine (ie, for i386, i486) */ +#endif + + /* generic routine */ + gettimeofday( &tv, NULL ); + counter->QuadPart = (LONGLONG)tv.tv_usec + (LONGLONG)tv.tv_sec * 1000000LL; return TRUE; } @@ -37,9 +115,20 @@ BOOL WINAPI QueryPerformanceCounter(PLARGE_INTEGER counter) */ BOOL WINAPI QueryPerformanceFrequency(PLARGE_INTEGER frequency) { - frequency->s.LowPart = 1000000; - frequency->s.HighPart = 0; + if (!QUERYPERF_Initialized) + QUERYPERF_Init(); + +#if defined(__i386__) && defined(__GNUC__) + if (QUERYPERF_RDTSC_Use) + { + frequency->QuadPart = QUERYPERF_RDTSC_Frequency; return TRUE; + } +#endif + + frequency->s.LowPart = 1000000; + frequency->s.HighPart = 0; + return TRUE; } /****************************************************************************