From: Tanner Gooding Date: Wed, 5 Aug 2020 22:16:05 +0000 (-0700) Subject: Adding support for X86Base.CpuId (#40167) X-Git-Tag: submit/tizen/20210909.063632~6199 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=96f178d32b7ba62485917ac46ef1edcfd3c2d10d;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Adding support for X86Base.CpuId (#40167) * Adding support for X86Base.CpuId * Rename getcpuid and getextcpuid to __cpuid and __cpuidex, respectively * Removing xchg from the Unix x64 __cpuid implementation * Add a comment as to why the X86/X86Base/CpuId test limits the checked vendors * Apply suggestions from code review Co-authored-by: Jan Kotas * Adding back a missing parentheses * Fixing a typo in the isGenuineIntel check * Avoid a conflict around cpuInfo * Avoid an implicit cast when comparing the cpuidInfo * Separate the __cpuidex qcall into coreclr and mono specific variants * Add the partial modifier to the X86Base.PlatformNotSupported.cs file Co-authored-by: Jan Kotas --- diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 501487c..1665f9a 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -224,6 +224,7 @@ + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.CoreCLR.cs new file mode 100644 index 0000000..e523487 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.CoreCLR.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Runtime.Intrinsics.X86 +{ + public abstract partial class X86Base + { + [DllImport(RuntimeHelpers.QCall)] + private static extern unsafe void __cpuidex(int* cpuInfo, int functionId, int subFunctionId); + } +} diff --git a/src/coreclr/src/classlibnative/bcltype/system.cpp b/src/coreclr/src/classlibnative/bcltype/system.cpp index c037236..6bd072f 100644 --- a/src/coreclr/src/classlibnative/bcltype/system.cpp +++ b/src/coreclr/src/classlibnative/bcltype/system.cpp @@ -607,9 +607,17 @@ BOOL QCALLTYPE SystemNative::WinRTSupported() #endif // FEATURE_COMINTEROP +#if defined(TARGET_X86) || defined(TARGET_AMD64) +void QCALLTYPE SystemNative::X86BaseCpuId(int cpuInfo[4], int functionId, int subFunctionId) +{ + QCALL_CONTRACT; + BEGIN_QCALL; + __cpuidex(cpuInfo, functionId, subFunctionId); + END_QCALL; +} - +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) diff --git a/src/coreclr/src/classlibnative/bcltype/system.h b/src/coreclr/src/classlibnative/bcltype/system.h index 20d357c..ff6720f 100644 --- a/src/coreclr/src/classlibnative/bcltype/system.h +++ b/src/coreclr/src/classlibnative/bcltype/system.h @@ -81,6 +81,10 @@ public: // Return a method info for the method were the exception was thrown static FCDECL1(ReflectMethodObject*, GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE); +#if defined(TARGET_X86) || defined(TARGET_AMD64) + static void QCALLTYPE X86BaseCpuId(int cpuInfo[4], int functionId, int subFunctionId); +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) + private: // Common processing code for FailFast static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode, STRINGREF errorSource); diff --git a/src/coreclr/src/vm/amd64/AsmHelpers.asm b/src/coreclr/src/vm/amd64/AsmHelpers.asm index cb5d1fa..172b800 100644 --- a/src/coreclr/src/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/src/vm/amd64/AsmHelpers.asm @@ -667,27 +667,6 @@ NESTED_ENTRY ProfileTailcallNaked, _TEXT NESTED_END ProfileTailcallNaked, _TEXT -;; extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16]); -NESTED_ENTRY getcpuid, _TEXT - - push_nonvol_reg rbx - push_nonvol_reg rsi - END_PROLOGUE - - mov eax, ecx ; first arg - mov rsi, rdx ; second arg (result) - xor ecx, ecx ; clear ecx - needed for "Structured Extended Feature Flags" - cpuid - mov [rsi+ 0], eax - mov [rsi+ 4], ebx - mov [rsi+ 8], ecx - mov [rsi+12], edx - pop rsi - pop rbx - ret -NESTED_END getcpuid, _TEXT - - ;; extern "C" DWORD __stdcall xmmYmmStateSupport(); LEAF_ENTRY xmmYmmStateSupport, _TEXT mov ecx, 0 ; Specify xcr0 @@ -703,30 +682,6 @@ LEAF_ENTRY xmmYmmStateSupport, _TEXT ret LEAF_END xmmYmmStateSupport, _TEXT -;The following function uses Deterministic Cache Parameter leafs to determine the cache hierarchy information on Prescott & Above platforms. -; This function takes 3 arguments: -; Arg1 is an input to ECX. Used as index to specify which cache level to return information on by CPUID. -; Arg1 is already passed in ECX on call to getextcpuid, so no explicit assignment is required; -; Arg2 is an input to EAX. For deterministic code enumeration, we pass in 4H in arg2. -; Arg3 is a pointer to the return dwbuffer -NESTED_ENTRY getextcpuid, _TEXT - push_nonvol_reg rbx - push_nonvol_reg rsi - END_PROLOGUE - - mov eax, edx ; second arg (input to EAX) - mov rsi, r8 ; third arg (pointer to return dwbuffer) - cpuid - mov [rsi+ 0], eax - mov [rsi+ 4], ebx - mov [rsi+ 8], ecx - mov [rsi+12], edx - pop rsi - pop rbx - - ret -NESTED_END getextcpuid, _TEXT - ; EXTERN_C void moveOWord(LPVOID* src, LPVOID* target); ; diff --git a/src/coreclr/src/vm/amd64/unixstubs.cpp b/src/coreclr/src/vm/amd64/unixstubs.cpp index 1de9b9a..517eea9 100644 --- a/src/coreclr/src/vm/amd64/unixstubs.cpp +++ b/src/coreclr/src/vm/amd64/unixstubs.cpp @@ -10,35 +10,26 @@ extern "C" PORTABILITY_ASSERT("Implement for PAL"); } - DWORD getcpuid(DWORD arg, unsigned char result[16]) + void __cpuid(int cpuInfo[4], int function_id) { - DWORD eax; - __asm(" xor %%ecx, %%ecx\n" \ - " cpuid\n" \ - " mov %%eax, 0(%[result])\n" \ - " mov %%ebx, 4(%[result])\n" \ - " mov %%ecx, 8(%[result])\n" \ - " mov %%edx, 12(%[result])\n" \ - : "=a"(eax) /*output in eax*/\ - : "a"(arg), [result]"r"(result) /*inputs - arg in eax, result in any register*/\ - : "rbx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */ - ); - return eax; + // Based on the Clang implementation provided in cpuid.h: + // https://github.com/llvm/llvm-project/blob/master/clang/lib/Headers/cpuid.h + + __asm(" cpuid\n" \ + : "=a"(cpuInfo[0]), "=b"(cpuInfo[1]), "=c"(cpuInfo[2]), "=d"(cpuInfo[3]) \ + : "0"(function_id) + ); } - DWORD getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16]) + void __cpuidex(int cpuInfo[4], int function_id, int subFunction_id) { - DWORD eax; + // Based on the Clang implementation provided in cpuid.h: + // https://github.com/llvm/llvm-project/blob/master/clang/lib/Headers/cpuid.h + __asm(" cpuid\n" \ - " mov %%eax, 0(%[result])\n" \ - " mov %%ebx, 4(%[result])\n" \ - " mov %%ecx, 8(%[result])\n" \ - " mov %%edx, 12(%[result])\n" \ - : "=a"(eax) /*output in eax*/\ - : "c"(arg1), "a"(arg2), [result]"r"(result) /*inputs - arg1 in ecx, arg2 in eax, result in any register*/\ - : "rbx", "edx", "memory" /* registers that are clobbered, *result is clobbered */ - ); - return eax; + : "=a"(cpuInfo[0]), "=b"(cpuInfo[1]), "=c"(cpuInfo[2]), "=d"(cpuInfo[3]) \ + : "0"(function_id), "2"(subFunction_id) + ); } DWORD xmmYmmStateSupport() diff --git a/src/coreclr/src/vm/cgensys.h b/src/coreclr/src/vm/cgensys.h index 7c73231..2167299 100644 --- a/src/coreclr/src/vm/cgensys.h +++ b/src/coreclr/src/vm/cgensys.h @@ -95,21 +95,22 @@ inline void GetSpecificCpuInfo(CORINFO_CPU * cpuInfo) #endif // !TARGET_X86 #if (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(CROSSGEN_COMPILE) -extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16]); -extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16]); +#ifdef TARGET_UNIX +// MSVC directly defines intrinsics for __cpuid and __cpuidex matching the below signatures +// We define matching signatures for use on Unix platforms. + +extern "C" void __stdcall __cpuid(int cpuInfo[4], int function_id); +extern "C" void __stdcall __cpuidex(int cpuInfo[4], int function_id, int subFunction_id); +#endif // TARGET_UNIX extern "C" DWORD __stdcall xmmYmmStateSupport(); #endif inline bool TargetHasAVXSupport() { #if (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(CROSSGEN_COMPILE) - unsigned char buffer[16]; - // All x86/AMD64 targets support cpuid. - (void) getcpuid(1, buffer); - // getcpuid executes cpuid with eax set to its first argument, and ecx cleared. - // It returns the resulting eax, ebx, ecx and edx (in that order) in buffer[]. - // The AVX feature is ECX bit 28. - return ((buffer[11] & 0x10) != 0); + int cpuInfo[4]; + __cpuid(cpuInfo, 0x00000001); // All x86/AMD64 targets support cpuid. + return ((cpuInfo[3] & (1 << 28)) != 0); // The AVX feature is ECX bit 28. #endif // (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(CROSSGEN_COMPILE) return false; } diff --git a/src/coreclr/src/vm/codeman.cpp b/src/coreclr/src/vm/codeman.cpp index a04894f..ea39db7 100644 --- a/src/coreclr/src/vm/codeman.cpp +++ b/src/coreclr/src/vm/codeman.cpp @@ -1308,115 +1308,110 @@ void EEJitManager::SetCpuInfo() // We will set the following flags: // CORJIT_FLAG_USE_SSE2 is required - // SSE - EDX bit 25 (buffer[15] & 0x02) - // SSE2 - EDX bit 26 (buffer[15] & 0x04) + // SSE - EDX bit 25 + // SSE2 - EDX bit 26 + // CORJIT_FLAG_USE_AES + // CORJIT_FLAG_USE_SSE2 + // AES - ECX bit 25 + // CORJIT_FLAG_USE_PCLMULQDQ + // CORJIT_FLAG_USE_SSE2 + // PCLMULQDQ - ECX bit 1 // CORJIT_FLAG_USE_SSE3 if the following feature bits are set (input EAX of 1) // CORJIT_FLAG_USE_SSE2 - // SSE3 - ECX bit 0 (buffer[8] & 0x01) + // SSE3 - ECX bit 0 // CORJIT_FLAG_USE_SSSE3 if the following feature bits are set (input EAX of 1) // CORJIT_FLAG_USE_SSE3 - // SSSE3 - ECX bit 9 (buffer[9] & 0x02) + // SSSE3 - ECX bit 9 // CORJIT_FLAG_USE_SSE41 if the following feature bits are set (input EAX of 1) // CORJIT_FLAG_USE_SSSE3 - // SSE4.1 - ECX bit 19 (buffer[10] & 0x08) + // SSE4.1 - ECX bit 19 // CORJIT_FLAG_USE_SSE42 if the following feature bits are set (input EAX of 1) // CORJIT_FLAG_USE_SSE41 - // SSE4.2 - ECX bit 20 (buffer[10] & 0x10) + // SSE4.2 - ECX bit 20 // CORJIT_FLAG_USE_POPCNT if the following feature bits are set (input EAX of 1) // CORJIT_FLAG_USE_SSE42 - // POPCNT - ECX bit 23 (buffer[10] & 0x80) + // POPCNT - ECX bit 23 // CORJIT_FLAG_USE_AVX if the following feature bits are set (input EAX of 1), and xmmYmmStateSupport returns 1: // CORJIT_FLAG_USE_SSE42 - // OSXSAVE - ECX bit 27 (buffer[11] & 0x08) + // OSXSAVE - ECX bit 27 + // AVX - ECX bit 28 // XGETBV - XCR0[2:1] 11b - // AVX - ECX bit 28 (buffer[11] & 0x10) // CORJIT_FLAG_USE_FMA if the following feature bits are set (input EAX of 1), and xmmYmmStateSupport returns 1: // CORJIT_FLAG_USE_AVX - // FMA - ECX bit 12 (buffer[9] & 0x10) + // FMA - ECX bit 12 // CORJIT_FLAG_USE_AVX2 if the following feature bit is set (input EAX of 0x07 and input ECX of 0): // CORJIT_FLAG_USE_AVX - // AVX2 - EBX bit 5 (buffer[4] & 0x20) + // AVX2 - EBX bit 5 // CORJIT_FLAG_USE_AVX_512 is not currently set, but defined so that it can be used in future without - // CORJIT_FLAG_USE_AES - // CORJIT_FLAG_USE_SSE2 - // AES - ECX bit 25 (buffer[11] & 0x01) - // CORJIT_FLAG_USE_PCLMULQDQ - // CORJIT_FLAG_USE_SSE2 - // PCLMULQDQ - ECX bit 1 (buffer[8] & 0x01) // CORJIT_FLAG_USE_BMI1 if the following feature bit is set (input EAX of 0x07 and input ECX of 0): - // BMI1 - EBX bit 3 (buffer[4] & 0x08) + // BMI1 - EBX bit 3 // CORJIT_FLAG_USE_BMI2 if the following feature bit is set (input EAX of 0x07 and input ECX of 0): - // BMI2 - EBX bit 8 (buffer[5] & 0x01) + // BMI2 - EBX bit 8 // CORJIT_FLAG_USE_LZCNT if the following feature bits are set (input EAX of 80000001H) - // LZCNT - ECX bit 5 (buffer[8] & 0x20) + // LZCNT - ECX bit 5 // synchronously updating VM and JIT. - unsigned char buffer[16]; - DWORD maxCpuId = getcpuid(0, buffer); + int cpuidInfo[4]; + + __cpuid(cpuidInfo, 0x00000000); + uint32_t maxCpuId = static_cast(cpuidInfo[0]); if (maxCpuId >= 1) { - // getcpuid executes cpuid with eax set to its first argument, and ecx cleared. - // It returns the resulting eax in buffer[0-3], ebx in buffer[4-7], ecx in buffer[8-11], - // and edx in buffer[12-15]. - - (void) getcpuid(1, buffer); + __cpuid(cpuidInfo, 0x00000001); - // If SSE/SSE2 is not enabled, there is no point in checking the rest. - // SSE is bit 25 of EDX (buffer[15] & 0x02) - // SSE2 is bit 26 of EDX (buffer[15] & 0x04) - - if ((buffer[15] & 0x06) == 0x06) // SSE & SSE2 + if (((cpuidInfo[3] & (1 << 25)) != 0) && ((cpuidInfo[3] & (1 << 26)) != 0)) // SSE & SSE2 { CPUCompileFlags.Set(InstructionSet_SSE); CPUCompileFlags.Set(InstructionSet_SSE2); - if ((buffer[11] & 0x02) != 0) // AESNI + + if ((cpuidInfo[2] & (1 << 25)) != 0) // AESNI { CPUCompileFlags.Set(InstructionSet_AES); } - if ((buffer[8] & 0x02) != 0) // PCLMULQDQ + if ((cpuidInfo[2] & (1 << 1)) != 0) // PCLMULQDQ { CPUCompileFlags.Set(InstructionSet_PCLMULQDQ); } - if ((buffer[8] & 0x01) != 0) // SSE3 + if ((cpuidInfo[2] & (1 << 0)) != 0) // SSE3 { CPUCompileFlags.Set(InstructionSet_SSE3); - if ((buffer[9] & 0x02) != 0) // SSSE3 + if ((cpuidInfo[2] & (1 << 9)) != 0) // SSSE3 { CPUCompileFlags.Set(InstructionSet_SSSE3); - if ((buffer[10] & 0x08) != 0) // SSE4.1 + if ((cpuidInfo[2] & (1 << 19)) != 0) // SSE4.1 { CPUCompileFlags.Set(InstructionSet_SSE41); - if ((buffer[10] & 0x10) != 0) // SSE4.2 + if ((cpuidInfo[2] & (1 << 20)) != 0) // SSE4.2 { CPUCompileFlags.Set(InstructionSet_SSE42); - if ((buffer[10] & 0x80) != 0) // POPCNT + if ((cpuidInfo[2] & (1 << 23)) != 0) // POPCNT { CPUCompileFlags.Set(InstructionSet_POPCNT); } - if ((buffer[11] & 0x18) == 0x18) // AVX & OSXSAVE + if (((cpuidInfo[2] & (1 << 27)) != 0) && ((cpuidInfo[2] & (1 << 28)) != 0)) // OSXSAVE & AVX { - if(DoesOSSupportAVX() && (xmmYmmStateSupport() == 1)) + if(DoesOSSupportAVX() && (xmmYmmStateSupport() == 1)) // XGETBV == 11 { CPUCompileFlags.Set(InstructionSet_AVX); - if ((buffer[9] & 0x10) != 0) // FMA + if ((cpuidInfo[2] & (1 << 12)) != 0) // FMA { CPUCompileFlags.Set(InstructionSet_FMA); } if (maxCpuId >= 0x07) { - (void) getextcpuid(0, 0x07, buffer); + __cpuidex(cpuidInfo, 0x00000007, 0x00000000); - if ((buffer[4] & 0x20) != 0) // AVX2 + if ((cpuidInfo[1] & (1 << 5)) != 0) // AVX2 { CPUCompileFlags.Set(InstructionSet_AVX2); } @@ -1443,31 +1438,28 @@ void EEJitManager::SetCpuInfo() if (maxCpuId >= 0x07) { - (void)getextcpuid(0, 0x07, buffer); + __cpuidex(cpuidInfo, 0x00000007, 0x00000000); - if ((buffer[4] & 0x08) != 0) // BMI1 + if ((cpuidInfo[2] & (1 << 3)) != 0) // BMI1 { CPUCompileFlags.Set(InstructionSet_BMI1); } - if ((buffer[5] & 0x01) != 0) // BMI2 + if ((cpuidInfo[2] & (1 << 8)) != 0) // BMI2 { CPUCompileFlags.Set(InstructionSet_BMI2); } } } - DWORD maxCpuIdEx = getcpuid(0x80000000, buffer); + __cpuid(cpuidInfo, 0x80000000); + uint32_t maxCpuIdEx = static_cast(cpuidInfo[0]); if (maxCpuIdEx >= 0x80000001) { - // getcpuid executes cpuid with eax set to its first argument, and ecx cleared. - // It returns the resulting eax in buffer[0-3], ebx in buffer[4-7], ecx in buffer[8-11], - // and edx in buffer[12-15]. - - (void) getcpuid(0x80000001, buffer); + __cpuid(cpuidInfo, 0x80000001); - if ((buffer[8] & 0x20) != 0) // LZCNT + if ((cpuidInfo[3] & (1 << 5)) != 0) // LZCNT { CPUCompileFlags.Set(InstructionSet_LZCNT); } diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index e4896f2..f1827fa 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -1088,6 +1088,12 @@ FCFuncStart(gPalOleAut32Funcs) FCFuncEnd() #endif +#if defined(TARGET_X86) || defined(TARGET_AMD64) +FCFuncStart(gX86BaseFuncs) + QCFuncElement("__cpuidex", SystemNative::X86BaseCpuId) +FCFuncEnd() +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) + #ifdef FEATURE_COMINTEROP // @@ -1235,6 +1241,10 @@ FCClassElement("WaitHandle", "System.Threading", gWaitHandleFuncs) FCClassElement("WeakReference", "System", gWeakReferenceFuncs) FCClassElement("WeakReference`1", "System", gWeakReferenceOfTFuncs) +#if defined(TARGET_X86) || defined(TARGET_AMD64) +FCClassElement("X86Base", "System.Runtime.Intrinsics.X86", gX86BaseFuncs) +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) + #if defined(FEATURE_EVENTSOURCE_XPLAT) FCClassElement("XplatEventLogger", "System.Diagnostics.Tracing", gEventLogger) #endif //defined(FEATURE_EVENTSOURCE_XPLAT) diff --git a/src/coreclr/src/vm/i386/cgenx86.cpp b/src/coreclr/src/vm/i386/cgenx86.cpp index 58cbf0f..75ff1b7 100644 --- a/src/coreclr/src/vm/i386/cgenx86.cpp +++ b/src/coreclr/src/vm/i386/cgenx86.cpp @@ -1139,54 +1139,6 @@ void ResumeAtJit(PCONTEXT pContext, LPVOID oldESP) #ifndef TARGET_UNIX #pragma warning(push) #pragma warning(disable: 4035) -extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16]) -{ - LIMITED_METHOD_CONTRACT - - __asm - { - push ebx - push esi - mov eax, arg - cpuid - mov esi, result - mov [esi+ 0], eax - mov [esi+ 4], ebx - mov [esi+ 8], ecx - mov [esi+12], edx - pop esi - pop ebx - } -} - -// The following function uses Deterministic Cache Parameter leafs to determine the cache hierarchy information on Prescott & Above platforms. -// This function takes 3 arguments: -// Arg1 is an input to ECX. Used as index to specify which cache level to return infoformation on by CPUID. -// Arg2 is an input to EAX. For deterministic code enumeration, we pass in 4H in arg2. -// Arg3 is a pointer to the return buffer -// No need to check whether or not CPUID is supported because we have already called CPUID with success to come here. - -extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16]) -{ - LIMITED_METHOD_CONTRACT - - __asm - { - push ebx - push esi - mov ecx, arg1 - mov eax, arg2 - cpuid - mov esi, result - mov [esi+ 0], eax - mov [esi+ 4], ebx - mov [esi+ 8], ecx - mov [esi+12], edx - pop esi - pop ebx - } -} - extern "C" DWORD __stdcall xmmYmmStateSupport() { // No CONTRACT @@ -1207,41 +1159,30 @@ extern "C" DWORD __stdcall xmmYmmStateSupport() done: } } - #pragma warning(pop) #else // !TARGET_UNIX -extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16]) +void __cpuid(int cpuInfo[4], int function_id) { - DWORD eax; - __asm(" xor %%ecx, %%ecx\n" \ - " cpuid\n" \ - " mov %%eax, 0(%[result])\n" \ - " mov %%ebx, 4(%[result])\n" \ - " mov %%ecx, 8(%[result])\n" \ - " mov %%edx, 12(%[result])\n" \ - : "=a"(eax) /*output in eax*/\ - : "a"(arg), [result]"r"(result) /*inputs - arg in eax, result in any register*/\ - : "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */ - ); - return eax; + // Based on the Clang implementation provided in cpuid.h: + // https://github.com/llvm/llvm-project/blob/master/clang/lib/Headers/cpuid.h + + __asm(" cpuid" + : "=a"(cpuInfo[0]), "=b"(cpuInfo[1]), "=c"(cpuInfo[2]), "=d"(cpuInfo[3]) \ + : "0"(function_id) + ); } -extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16]) +void __cpuidex(int cpuInfo[4], int function_id, int subFunction_id) { - DWORD eax; - DWORD ecx; - __asm(" cpuid\n" \ - " mov %%eax, 0(%[result])\n" \ - " mov %%ebx, 4(%[result])\n" \ - " mov %%ecx, 8(%[result])\n" \ - " mov %%edx, 12(%[result])\n" \ - : "=a"(eax), "=c"(ecx) /*output in eax, ecx is rewritten*/\ - : "c"(arg1), "a"(arg2), [result]"r"(result) /*inputs - arg1 in ecx, arg2 in eax, result in any register*/\ - : "ebx", "edx", "memory" /* registers that are clobbered, *result is clobbered */ - ); - return eax; + // Based on the Clang implementation provided in cpuid.h: + // https://github.com/llvm/llvm-project/blob/master/clang/lib/Headers/cpuid.h + + __asm(" cpuid" + : "=a"(cpuInfo[0]), "=b"(cpuInfo[1]), "=c"(cpuInfo[2]), "=d"(cpuInfo[3]) \ + : "0"(function_id), "2"(subFunction_id) + ); } extern "C" DWORD __stdcall xmmYmmStateSupport() diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.PlatformNotSupported.cs index 045a8a2..56ff44a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.PlatformNotSupported.cs @@ -11,17 +11,17 @@ namespace System.Runtime.Intrinsics.X86 /// This class provides access to Intel BMI1 hardware instructions via intrinsics /// [CLSCompliant(false)] - public abstract class Bmi1 // : X86Base + public abstract class Bmi1 : X86Base { internal Bmi1() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } /// /// unsigned __int64 _andn_u64 (unsigned __int64 a, unsigned __int64 b) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.cs index 535d5f7..82e501c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi1.cs @@ -10,18 +10,18 @@ namespace System.Runtime.Intrinsics.X86 /// [Intrinsic] [CLSCompliant(false)] - public abstract class Bmi1 // : X86Base + public abstract class Bmi1 : X86Base { internal Bmi1() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } [Intrinsic] - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } /// /// unsigned __int64 _andn_u64 (unsigned __int64 a, unsigned __int64 b) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.PlatformNotSupported.cs index 08a4204..8c1a66e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.PlatformNotSupported.cs @@ -11,17 +11,17 @@ namespace System.Runtime.Intrinsics.X86 /// This class provides access to Intel BMI2 hardware instructions via intrinsics /// [CLSCompliant(false)] - public abstract class Bmi2 // : X86Base + public abstract class Bmi2 : X86Base { internal Bmi2() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } /// /// unsigned __int64 _bzhi_u64 (unsigned __int64 a, unsigned int index) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.cs index 81dff57..95104e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Bmi2.cs @@ -10,18 +10,18 @@ namespace System.Runtime.Intrinsics.X86 /// [Intrinsic] [CLSCompliant(false)] - public abstract class Bmi2 // : X86Base + public abstract class Bmi2 : X86Base { internal Bmi2() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } [Intrinsic] - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } /// /// unsigned __int64 _bzhi_u64 (unsigned __int64 a, unsigned int index) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.PlatformNotSupported.cs index a800021..1c2eed2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.PlatformNotSupported.cs @@ -10,17 +10,17 @@ namespace System.Runtime.Intrinsics.X86 /// This class provides access to Intel LZCNT hardware instructions via intrinsics /// [CLSCompliant(false)] - public abstract class Lzcnt // : X86Base + public abstract class Lzcnt : X86Base { internal Lzcnt() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } /// /// unsigned __int64 _lzcnt_u64 (unsigned __int64 a) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs index d6d278c..55e8f73 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs @@ -10,18 +10,18 @@ namespace System.Runtime.Intrinsics.X86 /// [Intrinsic] [CLSCompliant(false)] - public abstract class Lzcnt // : X86Base + public abstract class Lzcnt : X86Base { internal Lzcnt() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } [Intrinsic] - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } /// /// unsigned __int64 _lzcnt_u64 (unsigned __int64 a) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.PlatformNotSupported.cs index 00d3c68..1d92f91 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.PlatformNotSupported.cs @@ -11,17 +11,17 @@ namespace System.Runtime.Intrinsics.X86 /// This class provides access to Intel SSE hardware instructions via intrinsics /// [CLSCompliant(false)] - public abstract class Sse // : X86Base + public abstract class Sse : X86Base { internal Sse() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { [Intrinsic] get { return false; } } + public static new bool IsSupported { [Intrinsic] get { return false; } } /// /// __int64 _mm_cvtss_si64 (__m128 a) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.cs index 1e88751..129f2ec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Sse.cs @@ -10,18 +10,18 @@ namespace System.Runtime.Intrinsics.X86 /// [Intrinsic] [CLSCompliant(false)] - public abstract class Sse // : X86Base + public abstract class Sse : X86Base { internal Sse() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } [Intrinsic] - public abstract class X64 // : X86Base.X64 + public new abstract class X64 : X86Base.X64 { internal X64() { } - public static bool IsSupported { get => IsSupported; } + public static new bool IsSupported { get => IsSupported; } /// /// __int64 _mm_cvtss_si64 (__m128 a) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs index a0e0e68..261ac82 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs @@ -9,13 +9,13 @@ namespace System.Runtime.Intrinsics.X86 /// /// This class provides access to the x86 base hardware instructions via intrinsics /// - internal abstract class X86Base + public abstract partial class X86Base { internal X86Base() { } public static bool IsSupported { [Intrinsic] get => false; } - internal abstract class X64 + public abstract class X64 { internal X64() { } @@ -65,5 +65,11 @@ namespace System.Runtime.Intrinsics.X86 /// Its functionality is exposed in the public class. /// internal static uint BitScanReverse(uint value) { throw new PlatformNotSupportedException(); } + + /// + /// void __cpuidex(int cpuInfo[4], int function_id, int subfunction_id); + /// CPUID + /// + public static (int Eax, int Ebx, int Ecx, int Edx) CpuId(int functionId, int subFunctionId) { throw new PlatformNotSupportedException(); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs index de49813..7f7576b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Runtime.Intrinsics.X86 { @@ -9,13 +10,17 @@ namespace System.Runtime.Intrinsics.X86 /// This class provides access to the x86 base hardware instructions via intrinsics /// [Intrinsic] - internal abstract class X86Base + public abstract partial class X86Base { + internal X86Base() { } + public static bool IsSupported { get => IsSupported; } [Intrinsic] - internal abstract class X64 + public abstract class X64 { + internal X64() { } + public static bool IsSupported { get => IsSupported; } /// @@ -62,5 +67,16 @@ namespace System.Runtime.Intrinsics.X86 /// Its functionality is exposed in the public class. /// internal static uint BitScanReverse(uint value) => BitScanReverse(value); + + /// + /// void __cpuidex(int cpuInfo[4], int function_id, int subfunction_id); + /// CPUID + /// + public static unsafe (int Eax, int Ebx, int Ecx, int Edx) CpuId(int functionId, int subFunctionId) + { + int* cpuInfo = stackalloc int[4]; + __cpuidex(cpuInfo, functionId, subFunctionId); + return (cpuInfo[0], cpuInfo[1], cpuInfo[2], cpuInfo[3]); + } } } diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 4fc9a2d..fbb78cb 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -3367,10 +3367,10 @@ namespace System.Runtime.Intrinsics.X86 } } [System.CLSCompliantAttribute(false)] - public abstract partial class Bmi1 + public abstract partial class Bmi1 : System.Runtime.Intrinsics.X86.X86Base { internal Bmi1() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static uint AndNot(uint left, uint right) { throw null; } public static uint BitFieldExtract(uint value, byte start, byte length) { throw null; } public static uint BitFieldExtract(uint value, ushort control) { throw null; } @@ -3378,10 +3378,10 @@ namespace System.Runtime.Intrinsics.X86 public static uint GetMaskUpToLowestSetBit(uint value) { throw null; } public static uint ResetLowestSetBit(uint value) { throw null; } public static uint TrailingZeroCount(uint value) { throw null; } - public abstract partial class X64 + public new abstract partial class X64 : System.Runtime.Intrinsics.X86.X86Base.X64 { internal X64() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static ulong AndNot(ulong left, ulong right) { throw null; } public static ulong BitFieldExtract(ulong value, byte start, byte length) { throw null; } public static ulong BitFieldExtract(ulong value, ushort control) { throw null; } @@ -3392,19 +3392,19 @@ namespace System.Runtime.Intrinsics.X86 } } [System.CLSCompliantAttribute(false)] - public abstract partial class Bmi2 + public abstract partial class Bmi2 : System.Runtime.Intrinsics.X86.X86Base { internal Bmi2() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static uint MultiplyNoFlags(uint left, uint right) { throw null; } public unsafe static uint MultiplyNoFlags(uint left, uint right, uint* low) { throw null; } public static uint ParallelBitDeposit(uint value, uint mask) { throw null; } public static uint ParallelBitExtract(uint value, uint mask) { throw null; } public static uint ZeroHighBits(uint value, uint index) { throw null; } - public abstract partial class X64 + public new abstract partial class X64 : System.Runtime.Intrinsics.X86.X86Base.X64 { internal X64() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static ulong MultiplyNoFlags(ulong left, ulong right) { throw null; } public unsafe static ulong MultiplyNoFlags(ulong left, ulong right, ulong* low) { throw null; } public static ulong ParallelBitDeposit(ulong value, ulong mask) { throw null; } @@ -3491,15 +3491,15 @@ namespace System.Runtime.Intrinsics.X86 } } [System.CLSCompliantAttribute(false)] - public abstract partial class Lzcnt + public abstract partial class Lzcnt : System.Runtime.Intrinsics.X86.X86Base { internal Lzcnt() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static uint LeadingZeroCount(uint value) { throw null; } - public abstract partial class X64 + public new abstract partial class X64 : System.Runtime.Intrinsics.X86.X86Base.X64 { internal X64() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static ulong LeadingZeroCount(ulong value) { throw null; } } } @@ -3530,10 +3530,10 @@ namespace System.Runtime.Intrinsics.X86 } } [System.CLSCompliantAttribute(false)] - public abstract partial class Sse + public abstract partial class Sse : System.Runtime.Intrinsics.X86.X86Base { internal Sse() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 AddScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 And(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } @@ -3621,10 +3621,10 @@ namespace System.Runtime.Intrinsics.X86 public static System.Runtime.Intrinsics.Vector128 UnpackHigh(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 UnpackLow(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 Xor(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public abstract partial class X64 + public new abstract partial class X64 : System.Runtime.Intrinsics.X86.X86Base.X64 { internal X64() { } - public static bool IsSupported { get { throw null; } } + public static new bool IsSupported { get { throw null; } } public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, long value) { throw null; } public static long ConvertToInt64(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static long ConvertToInt64WithTruncation(System.Runtime.Intrinsics.Vector128 value) { throw null; } @@ -4183,4 +4183,16 @@ namespace System.Runtime.Intrinsics.X86 public static new bool IsSupported { get { throw null; } } } } + [System.CLSCompliantAttribute(false)] + public abstract partial class X86Base + { + internal X86Base() { } + public static bool IsSupported { get { throw null; } } + public static (int Eax, int Ebx, int Ecx, int Edx) CpuId(int functionId, int subFunctionId) { throw null; } + public abstract partial class X64 + { + internal X64() { } + public static bool IsSupported { get { throw null; } } + } + } } diff --git a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj index 95e1e72..e2435e5 100644 --- a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -266,6 +266,7 @@ + diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.Mono.cs new file mode 100644 index 0000000..a4acdc9 --- /dev/null +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.Mono.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Intrinsics.X86 +{ + public abstract partial class X86Base + { + private static unsafe void __cpuidex(int* cpuInfo, int functionId, int subFunctionId) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs new file mode 100644 index 0000000..39dd182 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; + +namespace IntelHardwareIntrinsicTest +{ + class Program + { + const int Pass = 100; + const int Fail = 0; + + static unsafe int Main(string[] args) + { + int testResult = Pass; + + if (!X86Base.IsSupported) + { + return testResult; + } + + (int eax, int ebx, int ecx, int edx) = X86Base.CpuId(0x00000000, 0x00000000); + + bool isAuthenticAmd = (ebx == 0x68747541) && (ecx == 0x444D4163) && (edx == 0x69746E65); + bool isGenuineIntel = (ebx == 0x756E6547) && (ecx == 0x6C65746E) && (edx == 0x49656E69); + + if (!isAuthenticAmd && !isGenuineIntel) + { + // CPUID checks are vendor specific and aren't guaranteed to match up, even across Intel/AMD + // as such, we limit ourselves to just AuthenticAMD and GenuineIntel right now. Any other + // vendors would need to be validated against the checks below and added to the list as necessary. + + // An example of a difference is Intel/AMD for LZCNT. While the same underlying bit is used to + // represent presence of the LZCNT instruction, AMD began using this bit around 2007 for its + // ABM instruction set, which indicates LZCNT and POPCNT. Intel introduced a separate bit for + // POPCNT and didn't actually implement LZCNT and begin using the LZCNT bit until 2013. So + // while everything happens to line up today, it doesn't always and may not always do so. + + Console.WriteLine($"Unrecognized CPU vendor: EBX: {ebx:X8}, ECX: {ecx:X8}, EDX: {edx:X8}"); + testResult = Fail; + } + + int maxFunctionId = eax; + + if ((maxFunctionId < 0x00000001) || (Environment.GetEnvironmentVariable("COMPlus_EnableHWIntrinsic") is null)) + { + return testResult; + } + + (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000001, 0x00000000); + + if (IsBitIncorrect(ecx, 28, Avx.IsSupported, "AVX")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:AVX != Avx.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 25, Aes.IsSupported, "AES")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:AES != Aes.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 23, Popcnt.IsSupported, "POPCNT")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:POPCNT != Popcnt.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 20, Sse42.IsSupported, "SSE42")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:SSE42 != Sse42.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 19, Sse41.IsSupported, "SSE41")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:SSE41 != Sse41.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 12, Fma.IsSupported, "FMA")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:FMA != Fma.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 9, Ssse3.IsSupported, "SSSE3")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:SSSE3 != Ssse3.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 1, Pclmulqdq.IsSupported, "PCLMULQDQ")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:PCLMULQDQ != Pclmulqdq.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ecx, 0, Sse3.IsSupported, "SSE3")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:SSE3 != Sse3.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(edx, 26, Sse2.IsSupported, "SSE2")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:SSE2 != Sse2.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(edx, 25, Sse.IsSupported, "SSE")) + { + Console.WriteLine("CPUID Fn0000_0001_ECX:SSE != Sse.IsSupported"); + testResult = Fail; + } + + if (maxFunctionId < 0x00000007) + { + return testResult; + } + + (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000007, 0x00000000); + + if (IsBitIncorrect(ebx, 8, Bmi2.IsSupported, "BMI2")) + { + Console.WriteLine("CPUID Fn0000_0007_EBX:BMI2 != Bmi2.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ebx, 5, Avx2.IsSupported, "AVX2")) + { + Console.WriteLine("CPUID Fn0000_0007_EBX:AVX2 != Avx2.IsSupported"); + testResult = Fail; + } + + if (IsBitIncorrect(ebx, 3, Bmi1.IsSupported, "BMI1")) + { + Console.WriteLine("CPUID Fn0000_0001_EBX:BMI1 != Bmi1.IsSupported"); + testResult = Fail; + } + + (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)0x80000000), 0x00000000); + + if (isAuthenticAmd && ((ebx != 0x68747541) || (ecx != 0x444D4163) || (edx != 0x69746E65))) + { + Console.WriteLine("CPUID Fn8000_0000 reported different vendor info from Fn0000_0000"); + testResult = Fail; + } + + if (isGenuineIntel && ((ebx != 0x756E6547) && (ecx != 0x6C65746E) && (edx != 0x6C656E69))) + { + Console.WriteLine("CPUID Fn8000_0000 reported different vendor info from Fn0000_0000"); + testResult = Fail; + } + + int maxFunctionIdEx = eax; + + if (maxFunctionIdEx < 0x00000001) + { + return testResult; + } + + (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)0x80000001), 0x00000000); + + if (IsBitIncorrect(ecx, 5, Lzcnt.IsSupported, "LZCNT")) + { + Console.WriteLine("CPUID Fn8000_0001_ECX:LZCNT != Lzcnt.IsSupported"); + testResult = Fail; + } + + return testResult; + } + + static bool IsBitIncorrect(int register, int bitNumber, bool expectedResult, string name) + { + return ((register & (1 << bitNumber)) != ((expectedResult ? 1 : 0) << bitNumber)) + && (Environment.GetEnvironmentVariable($"COMPlus_Enable{name}") is null); + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId_r.csproj new file mode 100644 index 0000000..8c3ea60 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId_r.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + Embedded + + + + + + diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId_ro.csproj new file mode 100644 index 0000000..64875d4 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId_ro.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + Embedded + True + + + + + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_34587/Runtime_34587.cs b/src/tests/JIT/Regression/JitBlue/Runtime_34587/Runtime_34587.cs index 7cea12d..cb06c86 100644 --- a/src/tests/JIT/Regression/JitBlue/Runtime_34587/Runtime_34587.cs +++ b/src/tests/JIT/Regression/JitBlue/Runtime_34587/Runtime_34587.cs @@ -30,6 +30,7 @@ class Runtime_34587 TestLibrary.TestFramework.LogInformation($" SSE4.1: {Sse41.IsSupported}"); TestLibrary.TestFramework.LogInformation($" SSE4.2: {Sse42.IsSupported}"); TestLibrary.TestFramework.LogInformation($" SSSE3: {Ssse3.IsSupported}"); + TestLibrary.TestFramework.LogInformation($" X86Base: {X86Base.IsSupported}"); TestLibrary.TestFramework.LogInformation("Supported x64 ISAs:"); TestLibrary.TestFramework.LogInformation($" AES.X64: {X86Aes.X64.IsSupported}"); @@ -47,6 +48,7 @@ class Runtime_34587 TestLibrary.TestFramework.LogInformation($" SSE4.1.X64: {Sse41.X64.IsSupported}"); TestLibrary.TestFramework.LogInformation($" SSE4.2.X64: {Sse42.X64.IsSupported}"); TestLibrary.TestFramework.LogInformation($" SSSE3.X64: {Ssse3.X64.IsSupported}"); + TestLibrary.TestFramework.LogInformation($" X86Base.X64: {X86Base.X64.IsSupported}"); TestLibrary.TestFramework.LogInformation("Supported Arm ISAs:"); TestLibrary.TestFramework.LogInformation($" AdvSimd: {AdvSimd.IsSupported}"); @@ -240,6 +242,7 @@ class Runtime_34587 { bool succeeded = true; + succeeded &= ValidateX86Base(); succeeded &= ValidateSse(); succeeded &= ValidateSse2(); succeeded &= ValidateSse3(); @@ -258,19 +261,37 @@ class Runtime_34587 return succeeded; + static bool ValidateX86Base() + { + bool succeeded = true; + + if (X86Base.IsSupported) + { + succeeded &= (RuntimeInformation.OSArchitecture == Architecture.X86) || (RuntimeInformation.OSArchitecture == Architecture.X64); + } + + if (X86Base.X64.IsSupported) + { + succeeded &= X86Base.IsSupported; + succeeded &= (RuntimeInformation.OSArchitecture == Architecture.X64); + } + + return succeeded; + } + static bool ValidateSse() { bool succeeded = true; if (Sse.IsSupported) { - succeeded &= (RuntimeInformation.OSArchitecture == Architecture.X86) || (RuntimeInformation.OSArchitecture == Architecture.X64); + succeeded &= X86Base.IsSupported; } if (Sse.X64.IsSupported) { succeeded &= Sse.IsSupported; - succeeded &= (RuntimeInformation.OSArchitecture == Architecture.X64); + succeeded &= X86Base.X64.IsSupported; } return succeeded;