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 501487c6a111b..1665f9ac69250 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 0000000000000..e523487f0d6b6
--- /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 c037236f69596..6bd072f13ea84 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 20d357c17302d..ff6720f0a8c09 100644
--- a/src/coreclr/src/classlibnative/bcltype/system.h
+++ b/src/coreclr/src/classlibnative/bcltype/system.h
@@ -81,6 +81,10 @@ class SystemNative
// 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 cb5d1fad499d0..172b800f3feac 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 1de9b9a5b9d11..517eea98f6b6a 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 7c732316695e8..216729938ae14 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 a04894fa77edf..ea39db725e600 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 e4896f2000f63..f1827fa3bd97a 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 58cbf0ffa0015..75ff1b7dff17b 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 045a8a273b6da..56ff44adb7d9c 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 535d5f775720e..82e501ca97dea 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 08a4204301a5b..8c1a66ec97067 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 81dff57da5a0e..95104e6d2b92e 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 a800021bbab42..1c2eed20a3c87 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 d6d278c5d4c2f..55e8f737de8f1 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 00d3c684294ff..1d92f91621b66 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 1e88751c36870..129f2ecbd09a1 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 a0e0e68fbd032..261ac82076ecb 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 @@ internal X64() { }
/// 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 de49813d9de0a..7f7576be50ad2 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 @@ internal abstract class X64
/// 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 4fc9a2dab9e89..fbb78cb5533fe 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 @@ internal X64() { }
}
}
[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 @@ internal Bmi1() { }
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 @@ internal X64() { }
}
}
[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 @@ internal X64() { }
}
}
[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 @@ internal X64() { }
}
}
[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 @@ public unsafe static void StoreScalar(float* address, System.Runtime.Intrinsics.
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 @@ internal X64() { }
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 95e1e72845ad1..e2435e591fa60 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 0000000000000..a4acdc99f3509
--- /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 0000000000000..39dd182e4e0bf
--- /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 0000000000000..8c3ea60df00ca
--- /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 0000000000000..64875d4c153b2
--- /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 7cea12d04c9a2..cb06c86edf370 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 @@ public static int Main()
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 @@ public static int Main()
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 @@ public static bool ValidateX86()
{
bool succeeded = true;
+ succeeded &= ValidateX86Base();
succeeded &= ValidateSse();
succeeded &= ValidateSse2();
succeeded &= ValidateSse3();
@@ -258,19 +261,37 @@ public static bool ValidateX86()
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;