Fixing the InstructionSetDesc implications (#86486)
authorTanner Gooding <tagoo@outlook.com>
Fri, 2 Jun 2023 03:25:13 +0000 (20:25 -0700)
committerGitHub <noreply@github.com>
Fri, 2 Jun 2023 03:25:13 +0000 (20:25 -0700)
* Fixing the InstructionSetDesc implications

* Adding more NAOT smoke tests covering the missed instruction sets

* Simplify the HasInstructionSet(Avx512F) check in compSetProcessor

* Fixing the NAOT smoke tests

* Fixing some stale comments

* Fixing build failure

* Ensure the X86Serialize test lambda returns a bool

* Fixing build failure

* Ensure AVX2 isn't opportunistically supported and that dynamic checks work correctly for Vector128/256/512.IsHardwareAccelerated

* Ensure Avx512Vbmi has [Intrinsic] on the right members

* Fix the secondary isIsaSupported check to be properly opportunistic for NAOT

* Ensure vpermb is covered

* Allow opportunistic AvxVnni when Avx2 is opted into

* Don't expect opportunistic Avx2 or AvxVnni in the smoke tests

* Ensure Avx2.X64 checks ExpectedAvx2, not ExpectedAvx

* Change the filter the AVX512 NAOT smoke test on OSX

* Updating the CPUID test to cover new ISAs and correctly validate the hierarchy

* Add two more NAOT smoke tests which cover explicit ISA exclusion

* Add additional CpuId validation covering R2R scenarios using various ISA opt-in

* Ensure new R2R tests actually use R2R

* Don't try to expose an invalid --instruction-set combination

* Ensure xarch r2r tests only run on xarch

* Don't compare manufacturer name of CPUID 0x00000000 to 0x80000000

* Filter out the CPUID test on Mono and account for AVX-512 being unsupported on MacOS

28 files changed:
src/coreclr/inc/corinfoinstructionset.h
src/coreclr/inc/jiteeversionguid.h
src/coreclr/jit/compiler.cpp
src/coreclr/jit/compiler.h
src/coreclr/jit/hwintrinsic.cpp
src/coreclr/nativeaot/Runtime/IntrinsicConstants.h
src/coreclr/nativeaot/Runtime/startup.cpp
src/coreclr/tools/Common/InstructionSetHelpers.cs
src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs
src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs
src/coreclr/vm/codeman.cpp
src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.NoX86Intrinsics.xm [new file with mode: 0644]
src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs
src/tests/nativeaot/SmokeTests/HardwareIntrinsics/Program.cs
src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx.csproj [new file with mode: 0644]
src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx2.csproj [moved from src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64Vex.csproj with 91% similarity]
src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx512.csproj [new file with mode: 0644]
src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx_NoAvx2.csproj [new file with mode: 0644]
src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Baseline.csproj
src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Sse42.csproj [moved from src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64NonVex.csproj with 83% similarity]
src/tests/readytorun/HardwareIntrinsics/X86/CpuId.cs [new file with mode: 0644]
src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx.csproj [new file with mode: 0644]
src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx2.csproj [new file with mode: 0644]
src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx512.csproj [new file with mode: 0644]
src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx_NoAvx2.csproj [new file with mode: 0644]
src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Baseline.csproj [new file with mode: 0644]
src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Sse42.csproj [new file with mode: 0644]

index 8462ab3..d19a6ad 100644 (file)
@@ -566,24 +566,40 @@ inline CORINFO_InstructionSetFlags EnsureInstructionSetFlagsAreValid(CORINFO_Ins
             resultflags.RemoveInstructionSet(InstructionSet_X86Serialize);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX2))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_FMA))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512F_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512F_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512CD) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512CD);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512CD_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512CD))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512CD_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512CD_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512CD_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512BW) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512BW);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512BW_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512BW_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512DQ) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512DQ);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512DQ_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512DQ))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512DQ_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512DQ_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512DQ_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512VBMI) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512VBMI);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512VBMI_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512VBMI))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512VBMI_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512VBMI_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512VBMI_VL);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX512CD_VL))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX512DQ_VL))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
 #endif // TARGET_AMD64
 #ifdef TARGET_X86
         if (resultflags.HasInstructionSet(InstructionSet_SSE) && !resultflags.HasInstructionSet(InstructionSet_X86Base))
@@ -630,24 +646,40 @@ inline CORINFO_InstructionSetFlags EnsureInstructionSetFlagsAreValid(CORINFO_Ins
             resultflags.RemoveInstructionSet(InstructionSet_X86Serialize);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX2))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_FMA))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512F_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512F_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512CD) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512CD);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512CD_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512CD))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512CD_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512CD_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512CD_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512BW) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512BW);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512BW_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512BW_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512DQ) && !resultflags.HasInstructionSet(InstructionSet_AVX512F))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512DQ);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512DQ_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512DQ))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512DQ_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512DQ_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512F_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512DQ_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512VBMI) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512VBMI);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512VBMI_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512VBMI))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512VBMI_VL);
         if (resultflags.HasInstructionSet(InstructionSet_AVX512VBMI_VL) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL))
             resultflags.RemoveInstructionSet(InstructionSet_AVX512VBMI_VL);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX512BW_VL))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX512CD_VL))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
+        if (resultflags.HasInstructionSet(InstructionSet_AVX512F) && !resultflags.HasInstructionSet(InstructionSet_AVX512DQ_VL))
+            resultflags.RemoveInstructionSet(InstructionSet_AVX512F);
 #endif // TARGET_X86
 
     } while (!oldflags.Equals(resultflags));
index eee46d6..9bb39b0 100644 (file)
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
 #define GUID_DEFINED
 #endif // !GUID_DEFINED
 
-constexpr GUID JITEEVersionIdentifier = { /* dfc41bc9-f134-4c50-897e-fc9304a82059 */
-    0xdfc41bc9,
-    0xf134,
-    0x4c50,
-    {0x89, 0x7e, 0xfc, 0x93, 0x04, 0xa8, 0x20, 0x59}
+constexpr GUID JITEEVersionIdentifier = { /* d4414be1-70e4-46ac-8866-ca3a6c2f8422 */
+    0xd4414be1,
+    0x70e4,
+    0x46ac,
+    {0x88, 0x66, 0xca, 0x3a, 0x6c, 0x2f, 0x84, 0x22}
   };
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////
index b24bd63..c065161 100644 (file)
@@ -2285,15 +2285,16 @@ void Compiler::compSetProcessor()
     // the overall JIT implementation, we currently require the entire set of ISAs to be
     // supported and disable AVX512 support otherwise.
 
-    if (instructionSetFlags.HasInstructionSet(InstructionSet_AVX512BW_VL) &&
-        instructionSetFlags.HasInstructionSet(InstructionSet_AVX512CD_VL) &&
-        instructionSetFlags.HasInstructionSet(InstructionSet_AVX512DQ_VL))
+    if (instructionSetFlags.HasInstructionSet(InstructionSet_AVX512F))
     {
+        assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512F));
+        assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512F_VL));
         assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512BW));
+        assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512BW_VL));
         assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512CD));
+        assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512CD_VL));
         assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512DQ));
-        assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512F));
-        assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512F_VL));
+        assert(instructionSetFlags.HasInstructionSet(InstructionSet_AVX512DQ_VL));
 
         instructionSetFlags.AddInstructionSet(InstructionSet_Vector512);
 
@@ -2308,32 +2309,6 @@ void Compiler::compSetProcessor()
             preferredVectorByteLength = 256 / 8;
         }
     }
-    else
-    {
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512F);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512F_VL);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512BW);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512BW_VL);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512CD);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512CD_VL);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512DQ);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512DQ_VL);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512VBMI);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512VBMI_VL);
-
-#ifdef TARGET_AMD64
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512F_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512F_VL_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512BW_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512BW_VL_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512CD_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512CD_VL_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512DQ_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512DQ_VL_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512VBMI_X64);
-        instructionSetFlags.RemoveInstructionSet(InstructionSet_AVX512VBMI_VL_X64);
-#endif // TARGET_AMD64
-    }
 
     opts.preferredVectorByteLength = preferredVectorByteLength;
 #elif defined(TARGET_ARM64)
index bcb3fb9..6ba5e07 100644 (file)
@@ -8654,7 +8654,7 @@ private:
 
     // Get the number of bytes in a System.Numeric.Vector<T> for the current compilation.
     // Note - cannot be used for System.Runtime.Intrinsic
-    unsigned getVectorTByteLength()
+    uint32_t getVectorTByteLength()
     {
         // We need to report the ISA dependency to the VM so that scenarios
         // such as R2R work correctly for larger vector sizes, so we always
index 35d1b6a..24d37bc 100644 (file)
@@ -495,9 +495,9 @@ NamedIntrinsic HWIntrinsicInfo::lookupId(Compiler*         comp,
         return NI_Illegal;
     }
 
-    bool isIsaSupported = comp->compSupportsHWIntrinsic(isa);
-
+    bool isIsaSupported            = comp->compSupportsHWIntrinsic(isa);
     bool isHardwareAcceleratedProp = (strcmp(methodName, "get_IsHardwareAccelerated") == 0);
+
 #ifdef TARGET_XARCH
     if (isHardwareAcceleratedProp)
     {
@@ -541,22 +541,22 @@ NamedIntrinsic HWIntrinsicInfo::lookupId(Compiler*         comp,
         //
         // When the target hardware does support the instruction set, we can return a
         // constant true. When it doesn't then we want to report the check as dynamically
-        // supported instead. This allows some targets, such as AOT, to emit a check against
-        // a cached CPU query so lightup can still happen (such as for SSE4.1 when the target
-        // hardware is SSE2).
+        // supported instead if the opportunistic support does exist. This allows some targets,
+        // such as AOT, to emit a check against a cached CPU query so lightup can still happen
+        // (such as for SSE4.1 when the target hardware is SSE2).
         //
         // When the compiler doesn't support ISA or when it does but the target hardware does
         // not and we aren't in a scenario with support for a dynamic check, we want to return false.
 
-        if (isIsaSupported)
+        if (isIsaSupported && comp->compSupportsHWIntrinsic(isa))
         {
-            if (comp->compExactlyDependsOn(isa))
+            if (!comp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) || comp->compExactlyDependsOn(isa))
             {
                 return NI_IsSupported_True;
             }
-
-            if (comp->IsTargetAbi(CORINFO_NATIVEAOT_ABI))
+            else
             {
+                assert(comp->IsTargetAbi(CORINFO_NATIVEAOT_ABI));
                 return NI_IsSupported_Dynamic;
             }
         }
index 67ba547..ad7d2e1 100644 (file)
@@ -34,6 +34,7 @@ enum XArchIntrinsicConstants
     XArchIntrinsicConstants_Avx512dq_vl = 0x400000,
     XArchIntrinsicConstants_Avx512Vbmi = 0x800000,
     XArchIntrinsicConstants_Avx512Vbmi_vl = 0x1000000,
+    XArchIntrinsicConstants_Serialize = 0x2000000,
 };
 #endif //HOST_X86 || HOST_AMD64
 
index a178a11..7ce9836 100644 (file)
@@ -197,51 +197,57 @@ bool DetectCPUFeatures()
     {
         __cpuid(cpuidInfo, 0x00000001);
 
-        if (((cpuidInfo[CPUID_EDX] & (1 << 25)) != 0) && ((cpuidInfo[CPUID_EDX] & (1 << 26)) != 0))                     // SSE & SSE2
+        const int requiredBaselineEdxFlags = (1 << 25)                                                                  // SSE
+                                           | (1 << 26);                                                                 // SSE2
+
+        if ((cpuidInfo[CPUID_EDX] & requiredBaselineEdxFlags) == requiredBaselineEdxFlags)
         {
-            if ((cpuidInfo[CPUID_ECX] & (1 << 25)) != 0)                                                          // AESNI
+            if ((cpuidInfo[CPUID_ECX] & (1 << 25)) != 0)                                                                // AESNI
             {
                 g_cpuFeatures |= XArchIntrinsicConstants_Aes;
             }
 
-            if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0)                                                           // PCLMULQDQ
+            if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0)                                                                 // PCLMULQDQ
             {
                 g_cpuFeatures |= XArchIntrinsicConstants_Pclmulqdq;
             }
 
-            if ((cpuidInfo[CPUID_ECX] & (1 << 0)) != 0)                                                           // SSE3
+            if ((cpuidInfo[CPUID_ECX] & (1 << 0)) != 0)                                                                 // SSE3
             {
                 g_cpuFeatures |= XArchIntrinsicConstants_Sse3;
 
-                if ((cpuidInfo[CPUID_ECX] & (1 << 9)) != 0)                                                       // SSSE3
+                if ((cpuidInfo[CPUID_ECX] & (1 << 9)) != 0)                                                             // SSSE3
                 {
                     g_cpuFeatures |= XArchIntrinsicConstants_Ssse3;
 
-                    if ((cpuidInfo[CPUID_ECX] & (1 << 19)) != 0)                                                  // SSE4.1
+                    if ((cpuidInfo[CPUID_ECX] & (1 << 19)) != 0)                                                        // SSE4.1
                     {
                         g_cpuFeatures |= XArchIntrinsicConstants_Sse41;
 
-                        if ((cpuidInfo[CPUID_ECX] & (1 << 20)) != 0)                                              // SSE4.2
+                        if ((cpuidInfo[CPUID_ECX] & (1 << 20)) != 0)                                                    // SSE4.2
                         {
                             g_cpuFeatures |= XArchIntrinsicConstants_Sse42;
 
-                            if ((cpuidInfo[CPUID_ECX] & (1 << 22)) != 0)                                          // MOVBE
+                            if ((cpuidInfo[CPUID_ECX] & (1 << 22)) != 0)                                                // MOVBE
                             {
                                 g_cpuFeatures |= XArchIntrinsicConstants_Movbe;
                             }
 
-                            if ((cpuidInfo[CPUID_ECX] & (1 << 23)) != 0)                                          // POPCNT
+                            if ((cpuidInfo[CPUID_ECX] & (1 << 23)) != 0)                                                // POPCNT
                             {
                                 g_cpuFeatures |= XArchIntrinsicConstants_Popcnt;
                             }
 
-                            if (((cpuidInfo[CPUID_ECX] & (1 << 27)) != 0) && ((cpuidInfo[CPUID_ECX] & (1 << 28)) != 0)) // OSXSAVE & AVX
+                            const int requiredAvxEcxFlags = (1 << 27)                                                   // OSXSAVE
+                                                          | (1 << 28);                                                  // AVX
+
+                            if ((cpuidInfo[CPUID_ECX] & requiredAvxEcxFlags) == requiredAvxEcxFlags)
                             {
-                                if (PalIsAvxEnabled() && (xmmYmmStateSupport() == 1))
+                                if (PalIsAvxEnabled() && (xmmYmmStateSupport() == 1))                                   // XGETBV == 11
                                 {
                                     g_cpuFeatures |= XArchIntrinsicConstants_Avx;
 
-                                    if ((cpuidInfo[CPUID_ECX] & (1 << 12)) != 0)                                  // FMA
+                                    if ((cpuidInfo[CPUID_ECX] & (1 << 12)) != 0)                                        // FMA
                                     {
                                         g_cpuFeatures |= XArchIntrinsicConstants_Fma;
                                     }
@@ -250,66 +256,67 @@ bool DetectCPUFeatures()
                                     {
                                         __cpuidex(cpuidInfo, 0x00000007, 0x00000000);
 
-                                        if ((cpuidInfo[CPUID_EBX] & (1 << 5)) != 0)                               // AVX2
+                                        if ((cpuidInfo[CPUID_EBX] & (1 << 5)) != 0)                                     // AVX2
                                         {
                                             g_cpuFeatures |= XArchIntrinsicConstants_Avx2;
 
-                                            __cpuidex(cpuidInfo, 0x00000007, 0x00000001);
-                                            if ((cpuidInfo[CPUID_EAX] & (1 << 4)) != 0)                           // AVX-VNNI
-                                            {
-                                                g_cpuFeatures |= XArchIntrinsicConstants_AvxVnni;
-                                            }
-
-                                            if (PalIsAvx512Enabled() && (avx512StateSupport() == 1))       // XGETBV XRC0[7:5] == 111
+                                            if (PalIsAvx512Enabled() && (avx512StateSupport() == 1))                    // XGETBV XRC0[7:5] == 111
                                             {
-                                                if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0)                     // AVX512F
+                                                if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0)                            // AVX512F
                                                 {
                                                     g_cpuFeatures |= XArchIntrinsicConstants_Avx512f;
 
                                                     bool isAVX512_VLSupported = false;
-                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0)                 // AVX512VL
+                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0)                        // AVX512VL
                                                     {
                                                         g_cpuFeatures |= XArchIntrinsicConstants_Avx512f_vl;
                                                         isAVX512_VLSupported = true;
                                                     }
 
-                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0)                 // AVX512BW
+                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0)                        // AVX512BW
                                                     {
                                                         g_cpuFeatures |= XArchIntrinsicConstants_Avx512bw;
-                                                        if (isAVX512_VLSupported)
+                                                        if (isAVX512_VLSupported)                                       // AVX512BW_VL
                                                         {
                                                             g_cpuFeatures |= XArchIntrinsicConstants_Avx512bw_vl;
                                                         }
                                                     }
 
-                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0)                 // AVX512CD
+                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0)                        // AVX512CD
                                                     {
                                                         g_cpuFeatures |= XArchIntrinsicConstants_Avx512cd;
-                                                        if (isAVX512_VLSupported)
+                                                        if (isAVX512_VLSupported)                                       // AVX512CD_VL
                                                         {
                                                             g_cpuFeatures |= XArchIntrinsicConstants_Avx512cd_vl;
                                                         }
                                                     }
 
-                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0)                 // AVX512DQ
+                                                    if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0)                        // AVX512DQ
                                                     {
                                                         g_cpuFeatures |= XArchIntrinsicConstants_Avx512dq;
-                                                        if (isAVX512_VLSupported)
+                                                        if (isAVX512_VLSupported)                                       // AVX512DQ_VL
                                                         {
                                                             g_cpuFeatures |= XArchIntrinsicConstants_Avx512dq_vl;
                                                         }
                                                     }
 
-                                                    if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0)                  // AVX512VBMI
+                                                    if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0)                         // AVX512VBMI
                                                     {
                                                         g_cpuFeatures |= XArchIntrinsicConstants_Avx512Vbmi;
-                                                        if (isAVX512_VLSupported)
+                                                        if (isAVX512_VLSupported)                                       // AVX512VBMI_VL
                                                         {
                                                             g_cpuFeatures |= XArchIntrinsicConstants_Avx512Vbmi_vl;
                                                         }
                                                     }
                                                 }
                                             }
+
+                                            __cpuidex(cpuidInfo, 0x00000007, 0x00000001);
+
+                                            if ((cpuidInfo[CPUID_EAX] & (1 << 4)) != 0)                                 // AVX-VNNI
+                                            {
+                                                g_cpuFeatures |= XArchIntrinsicConstants_AvxVnni;
+                                            }
                                         }
                                     }
                                 }
@@ -333,6 +340,11 @@ bool DetectCPUFeatures()
             {
                 g_cpuFeatures |= XArchIntrinsicConstants_Bmi2;
             }
+
+            if ((cpuidInfo[CPUID_EDX] & (1 << 14)) != 0)
+            {
+                g_cpuFeatures |= XArchIntrinsicConstants_Serialize;                                               // SERIALIZE
+            }
         }
     }
 
index 8aa5705..93b04a1 100644 (file)
@@ -93,15 +93,42 @@ namespace System.CommandLine
                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("movbe");
                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt");
                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt");
+                optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("serialize");
 
                 // If AVX was enabled, we can opportunistically enable instruction sets which use the VEX encodings
                 Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX);
                 if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX))
                 {
+                    // TODO: Enable optimistic usage of AVX2 once we validate it doesn't break Vector<T> usage
+                    // optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx2");
+
+                    if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX2))
+                    {
+                        optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni");
+                    }
+
                     optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma");
                     optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi");
                     optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2");
-                    optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni");
+                }
+
+                if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX2))
+                {
+                }
+
+                Debug.Assert(InstructionSet.X64_AVX512F == InstructionSet.X86_AVX512F);
+                if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512F))
+                {
+                    Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512F_VL));
+                    Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512BW));
+                    Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512BW_VL));
+                    Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512CD));
+                    Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512CD_VL));
+                    Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512DQ));
+                    Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512DQ_VL));
+
+                    optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512vbmi");
+                    optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512vbmi_vl");
                 }
             }
             else if (targetArchitecture == TargetArchitecture.ARM64)
@@ -111,6 +138,9 @@ namespace System.CommandLine
                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1");
                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2");
                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse");
+                optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("dotprod");
+                optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rdma");
+                optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rcpc");
             }
 
             optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _,
index df6c062..1557059 100644 (file)
@@ -690,24 +690,40 @@ namespace Internal.JitInterface
                         resultflags.AddInstructionSet(InstructionSet.X64_X86Base);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX2);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
+                        resultflags.AddInstructionSet(InstructionSet.X64_FMA);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512CD))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512CD_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512CD);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512CD_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512BW))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512BW_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512BW);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512BW_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512DQ))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512DQ_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512DQ);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512DQ_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512VBMI))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512BW);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512VBMI_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512VBMI);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512VBMI_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512BW_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512BW_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512CD_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512DQ_VL);
                     break;
 
                 case TargetArchitecture.X86:
@@ -755,24 +771,40 @@ namespace Internal.JitInterface
                         resultflags.AddInstructionSet(InstructionSet.X86_X86Base);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX2);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
+                        resultflags.AddInstructionSet(InstructionSet.X86_FMA);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512CD))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512CD_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512CD);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512CD_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512BW))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512BW_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512BW);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512BW_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512DQ))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512DQ_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512DQ);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512DQ_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512VBMI))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512BW);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512VBMI_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512VBMI);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512VBMI_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512BW_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512BW_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512CD_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512DQ_VL);
                     break;
                 }
             } while (!oldflags.Equals(resultflags));
@@ -935,24 +967,40 @@ namespace Internal.JitInterface
                         resultflags.AddInstructionSet(InstructionSet.X64_X86Serialize);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX2))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_FMA))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512CD);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512CD))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512CD_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512CD_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512BW);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512BW))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512BW_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512BW_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512DQ);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512DQ))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512DQ_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512DQ_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512BW))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512VBMI);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512VBMI))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512VBMI_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512BW_VL))
                         resultflags.AddInstructionSet(InstructionSet.X64_AVX512VBMI_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512BW_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512CD_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
+                    if (resultflags.HasInstructionSet(InstructionSet.X64_AVX512DQ_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X64_AVX512F);
                     break;
 
                 case TargetArchitecture.X86:
@@ -1000,24 +1048,40 @@ namespace Internal.JitInterface
                         resultflags.AddInstructionSet(InstructionSet.X86_X86Serialize);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX2))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_FMA))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512F_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512CD);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512CD))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512CD_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512CD_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512BW);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512BW))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512BW_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512BW_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512DQ);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512DQ))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512DQ_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512F_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512DQ_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512BW))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512VBMI);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512VBMI))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512VBMI_VL);
                     if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512BW_VL))
                         resultflags.AddInstructionSet(InstructionSet.X86_AVX512VBMI_VL);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512BW_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512CD_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
+                    if (resultflags.HasInstructionSet(InstructionSet.X86_AVX512DQ_VL))
+                        resultflags.AddInstructionSet(InstructionSet.X86_AVX512F);
                     break;
                 }
             } while (!oldflags.Equals(resultflags));
@@ -1035,8 +1099,8 @@ namespace Internal.JitInterface
                 { ("x86-x64-v3", TargetArchitecture.X86),   "x86-x64-v2 avx2 bmi bmi2 lzcnt movbe fma" },
                 { ("skylake",    TargetArchitecture.X64),   "x86-x64-v3" },
                 { ("skylake",    TargetArchitecture.X86),   "x86-x64-v3" },
-                { ("x86-x64-v4", TargetArchitecture.X64),   "x86-x64-v3 avx512f avx512f_vl avx512bw avx512bw_vl avx512cd avx512cd_vl" },
-                { ("x86-x64-v4", TargetArchitecture.X86),   "x86-x64-v3 avx512f avx512f_vl avx512bw avx512bw_vl avx512cd avx512cd_vl" },
+                { ("x86-x64-v4", TargetArchitecture.X64),   "x86-x64-v3 avx512f avx512f_vl avx512bw avx512bw_vl avx512cd avx512cd_vl avx512dq avx512dq_vl" },
+                { ("x86-x64-v4", TargetArchitecture.X86),   "x86-x64-v3 avx512f avx512f_vl avx512bw avx512bw_vl avx512cd avx512cd_vl avx512dq avx512dq_vl" },
                 { ("armv8-a",    TargetArchitecture.ARM64), "neon" },
                 { ("armv8.1-a",  TargetArchitecture.ARM64), "armv8-a lse crc rdma" },
                 { ("armv8.2-a",  TargetArchitecture.ARM64), "armv8.1-a" },
index d5f7427..1b8b2c1 100644 (file)
@@ -115,16 +115,31 @@ implication        ,X86   ,AVXVNNI              ,AVX2
 implication        ,X86   ,MOVBE                ,SSE42
 implication        ,X86   ,X86Serialize         ,X86Base
 implication        ,X86   ,AVX512F              ,AVX2
+implication        ,X86   ,AVX512F              ,FMA
 implication        ,X86   ,AVX512F_VL           ,AVX512F
 implication        ,X86   ,AVX512CD             ,AVX512F
+implication        ,X86   ,AVX512CD_VL          ,AVX512CD
 implication        ,X86   ,AVX512CD_VL          ,AVX512F_VL
 implication        ,X86   ,AVX512BW             ,AVX512F
+implication        ,X86   ,AVX512BW_VL          ,AVX512BW
 implication        ,X86   ,AVX512BW_VL          ,AVX512F_VL
 implication        ,X86   ,AVX512DQ             ,AVX512F
+implication        ,X86   ,AVX512DQ_VL          ,AVX512DQ
 implication        ,X86   ,AVX512DQ_VL          ,AVX512F_VL
 implication        ,X86   ,AVX512VBMI           ,AVX512BW
+implication        ,X86   ,AVX512VBMI_VL        ,AVX512VBMI
 implication        ,X86   ,AVX512VBMI_VL        ,AVX512BW_VL
 
+; While the AVX-512 ISAs can be individually lit-up, they really
+; need F, BW, CD, DQ, and VL to be fully functional without adding
+; significant complexity into the JIT. Additionally, unlike AVX/AVX2
+; there was never really any hardware that didn't provide all 5 at
+; once, with the notable exception being Knight's Landing which
+; provided a similar but not quite the same feature.
+implication        ,X86   ,AVX512F              ,AVX512BW_VL
+implication        ,X86   ,AVX512F              ,AVX512CD_VL
+implication        ,X86   ,AVX512F              ,AVX512DQ_VL
+
 ; Definition of X64 instruction sets
 definearch         ,X64   ,64Bit     ,X64, X64
 
@@ -176,7 +191,7 @@ instructionsetgroup  ,x86-x64                    ,X64 X86  ,sse2
 instructionsetgroup  ,x86-x64-v2                 ,X64 X86  ,sse4.2 popcnt
 instructionsetgroup  ,x86-x64-v3                 ,X64 X86  ,x86-x64-v2 avx2 bmi bmi2 lzcnt movbe fma
 instructionsetgroup  ,skylake                    ,X64 X86  ,x86-x64-v3
-instructionsetgroup  ,x86-x64-v4                 ,X64 X86  ,x86-x64-v3 avx512f avx512f_vl avx512bw avx512bw_vl avx512cd avx512cd_vl
+instructionsetgroup  ,x86-x64-v4                 ,X64 X86  ,x86-x64-v3 avx512f avx512f_vl avx512bw avx512bw_vl avx512cd avx512cd_vl avx512dq avx512dq_vl
 
 instructionsetgroup  ,armv8-a                    ,ARM64    ,neon
 instructionsetgroup  ,armv8.1-a                  ,ARM64    ,armv8-a lse crc rdma
index 1411ca7..b7e4ee1 100644 (file)
@@ -111,6 +111,7 @@ namespace ILCompiler
             public const int Avx512dq_vl = 0x400000;
             public const int Avx512Vbmi = 0x800000;
             public const int Avx512Vbmi_vl = 0x1000000;
+            public const int Serialize = 0x2000000;
 
             public static int FromInstructionSet(InstructionSet instructionSet)
             {
@@ -170,6 +171,8 @@ namespace ILCompiler
                     InstructionSet.X64_AVX512VBMI_X64 => Avx512Vbmi,
                     InstructionSet.X64_AVX512VBMI_VL => Avx512Vbmi_vl,
                     InstructionSet.X64_AVX512VBMI_VL_X64 => Avx512Vbmi_vl,
+                    InstructionSet.X64_X86Serialize => Serialize,
+                    InstructionSet.X64_X86Serialize_X64 => Serialize,
 
                     // SSE and SSE2 are baseline ISAs - they're always available
                     InstructionSet.X64_SSE => 0,
index eba0c2c..cea5890 100644 (file)
@@ -1347,84 +1347,6 @@ void EEJitManager::SetCpuInfo()
     //   AMD64 Architecture Programmer’s Manual. Volume 3
     // For more information, please refer to the CPUID instruction in the respective manuals
 
-    // We will set the following flags:
-    //   CORJIT_FLAG_USE_SSE2 is required
-    //      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
-    //   CORJIT_FLAG_USE_SSSE3 if the following feature bits are set (input EAX of 1)
-    //      CORJIT_FLAG_USE_SSE3
-    //      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
-    //   CORJIT_FLAG_USE_SSE42 if the following feature bits are set (input EAX of 1)
-    //      CORJIT_FLAG_USE_SSE41
-    //      SSE4.2    - ECX bit 20
-    //   CORJIT_FLAG_USE_MOVBE if the following feature bits are set (input EAX of 1)
-    //      CORJIT_FLAG_USE_SSE42
-    //      MOVBE    - ECX bit 22
-    //   CORJIT_FLAG_USE_POPCNT if the following feature bits are set (input EAX of 1)
-    //      CORJIT_FLAG_USE_SSE42
-    //      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
-    //      AVX       - ECX bit 28
-    //      XGETBV    - XCR0[2:1]    11b
-    //   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
-    //   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
-    //   CORJIT_FLAG_USE_AVXVNNI if the following feature bit is set (input EAX of 0x07 and input ECX of 1):
-    //      CORJIT_FLAG_USE_AVX2
-    //      AVXVNNI   - EAX bit 4
-    //   CORJIT_FLAG_USE_AVX_512F if the following feature bit is set (input EAX of 0x07 and input ECX of 0), and avx512StateSupport returns 1:
-    //      CORJIT_FLAG_USE_AVX2
-    //      AVX512F   - EBX bit 16
-    //      XGETBV    - XRC0[7:5]    111b
-    //   CORJIT_FLAG_USE_AVX_512F_VL if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F
-    //      AVX512VL   - EBX bit 31
-    //   CORJIT_FLAG_USE_AVX_512BW if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F
-    //      AVX512BW   - EBX bit 30
-    //   CORJIT_FLAG_USE_AVX_512BW_VL if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F_VL
-    //      CORJIT_FLAG_USE_AVX_512BW
-    //   CORJIT_FLAG_USE_AVX_512CD if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F
-    //      AVX512CD   - EBX bit 28
-    //   CORJIT_FLAG_USE_AVX_512CD_VL if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F_VL
-    //      CORJIT_FLAG_USE_AVX_512CD
-    //   CORJIT_FLAG_USE_AVX_512DQ if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F
-    //      AVX512DQ   - EBX bit 7
-    //   CORJIT_FLAG_USE_AVX_512DQ_VL if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F_VL
-    //      CORJIT_FLAG_USE_AVX_512DQ
-    //   CORJIT_FLAG_USE_AVX_512VBMI if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      CORJIT_FLAG_USE_AVX512F
-    //      AVX512VBMI - ECX bit 1
-    //   CORJIT_FLAG_USE_BMI1 if the following feature bit is set (input EAX of 0x07 and input ECX of 0):
-    //      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
-    //   CORJIT_FLAG_USE_LZCNT if the following feature bits are set (input EAX of 80000001H)
-    //      LZCNT - ECX bit 5
-    // synchronously updating VM and JIT.
-
     union XarchCpuInfo
     {
         struct {
@@ -1516,9 +1438,12 @@ void EEJitManager::SetCpuInfo()
                         CPUCompileFlags.Set(InstructionSet_POPCNT);
                     }
 
-                    if (((cpuidInfo[CPUID_ECX] & (1 << 27)) != 0) && ((cpuidInfo[CPUID_ECX] & (1 << 28)) != 0)) // OSXSAVE & AVX
+                    const int requiredAvxEcxFlags = (1 << 27)                                             // OSXSAVE
+                                                  | (1 << 28);                                            // AVX
+
+                    if ((cpuidInfo[CPUID_ECX] & requiredAvxEcxFlags) == requiredAvxEcxFlags)
                     {
-                        if(DoesOSSupportAVX() && (xmmYmmStateSupport() == 1))                       // XGETBV == 11
+                        if(DoesOSSupportAVX() && (xmmYmmStateSupport() == 1))                             // XGETBV == 11
                         {
                             CPUCompileFlags.Set(InstructionSet_AVX);
 
@@ -1535,50 +1460,50 @@ void EEJitManager::SetCpuInfo()
                                 {
                                     CPUCompileFlags.Set(InstructionSet_AVX2);
 
-                                    if (DoesOSSupportAVX512() && (avx512StateSupport() == 1))      // XGETBV XRC0[7:5] == 111
+                                    if (DoesOSSupportAVX512() && (avx512StateSupport() == 1))             // XGETBV XRC0[7:5] == 111
                                     {
-                                        if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0)                     // AVX512F
+                                        if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0)                      // AVX512F
                                         {
                                             CPUCompileFlags.Set(InstructionSet_AVX512F);
 
                                             bool isAVX512_VLSupported = false;
-                                            if ((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0)                 // AVX512VL
+                                            if ((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0)                  // AVX512VL
                                             {
                                                 CPUCompileFlags.Set(InstructionSet_AVX512F_VL);
                                                 isAVX512_VLSupported = true;
                                             }
 
-                                            if ((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0)                 // AVX512BW
+                                            if ((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0)                  // AVX512BW
                                             {
                                                 CPUCompileFlags.Set(InstructionSet_AVX512BW);
-                                                if (isAVX512_VLSupported)                          // AVX512BW_VL
+                                                if (isAVX512_VLSupported)                                 // AVX512BW_VL
                                                 {
                                                     CPUCompileFlags.Set(InstructionSet_AVX512BW_VL);
                                                 }
                                             }
 
-                                            if ((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0)                 // AVX512CD
+                                            if ((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0)                  // AVX512CD
                                             {
                                                 CPUCompileFlags.Set(InstructionSet_AVX512CD);
-                                                if (isAVX512_VLSupported)                          // AVX512CD_VL
+                                                if (isAVX512_VLSupported)                                 // AVX512CD_VL
                                                 {
                                                     CPUCompileFlags.Set(InstructionSet_AVX512CD_VL);
                                                 }
                                             }
 
-                                            if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0)                 // AVX512DQ
+                                            if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0)                  // AVX512DQ
                                             {
                                                 CPUCompileFlags.Set(InstructionSet_AVX512DQ);
-                                                if (isAVX512_VLSupported)                          // AVX512DQ_VL
+                                                if (isAVX512_VLSupported)                                 // AVX512DQ_VL
                                                 {
                                                     CPUCompileFlags.Set(InstructionSet_AVX512DQ_VL);
                                                 }
                                             }
 
-                                            if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0)                  // AVX512VBMI
+                                            if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0)                   // AVX512VBMI
                                             {
                                                 CPUCompileFlags.Set(InstructionSet_AVX512VBMI);
-                                                if (isAVX512_VLSupported)                          // AVX512VBMI_VL
+                                                if (isAVX512_VLSupported)                                 // AVX512VBMI_VL
                                                 {
                                                     CPUCompileFlags.Set(InstructionSet_AVX512VBMI_VL);
                                                 }
@@ -1610,19 +1535,19 @@ void EEJitManager::SetCpuInfo()
     {
         __cpuidex(cpuidInfo, 0x00000007, 0x00000000);
 
-        if ((cpuidInfo[CPUID_EBX] & (1 << 3)) != 0)                                                           // BMI1
+        if ((cpuidInfo[CPUID_EBX] & (1 << 3)) != 0)                                                       // BMI1
         {
             CPUCompileFlags.Set(InstructionSet_BMI1);
         }
 
-        if ((cpuidInfo[CPUID_EBX] & (1 << 8)) != 0)                                                           // BMI2
+        if ((cpuidInfo[CPUID_EBX] & (1 << 8)) != 0)                                                       // BMI2
         {
             CPUCompileFlags.Set(InstructionSet_BMI2);
         }
 
         if ((cpuidInfo[CPUID_EDX] & (1 << 14)) != 0)
         {
-            CPUCompileFlags.Set(InstructionSet_X86Serialize);                                            // SERIALIZE
+            CPUCompileFlags.Set(InstructionSet_X86Serialize);                                             // SERIALIZE
         }
     }
 
@@ -1633,7 +1558,7 @@ void EEJitManager::SetCpuInfo()
     {
         __cpuid(cpuidInfo, 0x80000001);
 
-        if ((cpuidInfo[CPUID_ECX] & (1 << 5)) != 0)                                                               // LZCNT
+        if ((cpuidInfo[CPUID_ECX] & (1 << 5)) != 0)                                                       // LZCNT
         {
             CPUCompileFlags.Set(InstructionSet_LZCNT);
         }
diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.NoX86Intrinsics.xm b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.NoX86Intrinsics.xm
new file mode 100644 (file)
index 0000000..e69de29
index abef01e..7044a90 100644 (file)
@@ -3,10 +3,12 @@
 //
 
 using System;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics.X86;
 using System.Runtime.Intrinsics;
+using System.Reflection;
 using Xunit;
 
 namespace IntelHardwareIntrinsicTest._CpuId
@@ -17,6 +19,7 @@ namespace IntelHardwareIntrinsicTest._CpuId
         const int Fail = 0;
 
         [Fact]
+        [SkipOnMono("Mono does not currently have full support for intrinsics on xarch", TestPlatforms.Any)]
         public unsafe static void CpuId()
         {
             int testResult = Pass;
@@ -48,82 +51,88 @@ namespace IntelHardwareIntrinsicTest._CpuId
                 testResult = Fail;
             }
 
-            int maxFunctionId = eax;
+            uint maxFunctionId = (uint)eax;
 
-            if ((maxFunctionId < 0x00000001) || (Environment.GetEnvironmentVariable("DOTNET_EnableHWIntrinsic") is null))
+            if (maxFunctionId < 0x00000001)
             {
                 Assert.Equal(Pass, testResult);
                 return;
             }
 
+            bool isX86BaseDisabled = !GetDotnetEnable("HWINTRINSIC");
+            bool isHierarchyDisabled = isX86BaseDisabled;
+
             (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000001, 0x00000000);
 
-            if (IsBitIncorrect(ecx, 28, Avx.IsSupported, "AVX"))
+            if (IsBitIncorrect(edx, 25, typeof(Sse), Sse.IsSupported, "SSE", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:AVX != Avx.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 25, Aes.IsSupported, "AES"))
+            if (IsBitIncorrect(edx, 26, typeof(Sse2), Sse2.IsSupported, "SSE2", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:AES != Aes.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 23, Popcnt.IsSupported, "POPCNT"))
+            bool isSse2HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 25, typeof(Aes), Aes.IsSupported, "AES", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:POPCNT != Popcnt.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 20, Sse42.IsSupported, "SSE42"))
+            isHierarchyDisabled = isSse2HierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 1, typeof(Pclmulqdq), Pclmulqdq.IsSupported, "PCLMULQDQ", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:SSE42 != Sse42.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 19, Sse41.IsSupported, "SSE41"))
+            isHierarchyDisabled = isSse2HierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 0, typeof(Sse3), Sse3.IsSupported, "SSE3", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:SSE41 != Sse41.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 12, Fma.IsSupported, "FMA"))
+            if (IsBitIncorrect(ecx, 9, typeof(Ssse3), Ssse3.IsSupported, "SSSE3", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:FMA != Fma.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 9, Ssse3.IsSupported, "SSSE3"))
+            if (IsBitIncorrect(ecx, 19, typeof(Sse41), Sse41.IsSupported, "SSE41", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:SSSE3 != Ssse3.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 1, Pclmulqdq.IsSupported, "PCLMULQDQ"))
+            if (IsBitIncorrect(ecx, 20, typeof(Sse42), Sse42.IsSupported, "SSE42", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:PCLMULQDQ != Pclmulqdq.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ecx, 0, Sse3.IsSupported, "SSE3"))
+            bool isSse42HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 23, typeof(Popcnt), Popcnt.IsSupported, "POPCNT", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:SSE3 != Sse3.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(edx, 26, Sse2.IsSupported, "SSE2"))
+            isHierarchyDisabled = isSse42HierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 28, typeof(Avx), Avx.IsSupported, "AVX", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:SSE2 != Sse2.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(edx, 25, Sse.IsSupported, "SSE"))
+            bool isAvxHierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 12, typeof(Fma), Fma.IsSupported, "FMA", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_ECX:SSE != Sse.IsSupported");
                 testResult = Fail;
             }
 
+            bool isFmaHierarchyDisabled = isHierarchyDisabled;
+
             if (maxFunctionId < 0x00000007)
             {
                 Assert.Equal(Pass, testResult);
@@ -132,39 +141,109 @@ namespace IntelHardwareIntrinsicTest._CpuId
 
             (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000007, 0x00000000);
 
-            if (IsBitIncorrect(ebx, 8, Bmi2.IsSupported, "BMI2"))
+            isHierarchyDisabled = isAvxHierarchyDisabled;
+
+            if (IsBitIncorrect(ebx, 5, typeof(Avx2), Avx2.IsSupported, "AVX2", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0007_EBX:BMI2 != Bmi2.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ebx, 5, Avx2.IsSupported, "AVX2"))
+            bool isAvx2HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ebx, 3, typeof(Bmi1), Bmi1.IsSupported, "BMI1", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0007_EBX:AVX2 != Avx2.IsSupported");
                 testResult = Fail;
             }
 
-            if (IsBitIncorrect(ebx, 3, Bmi1.IsSupported, "BMI1"))
+            isHierarchyDisabled = isAvx2HierarchyDisabled;
+
+            if (IsBitIncorrect(ebx, 8, typeof(Bmi2), Bmi2.IsSupported, "BMI2", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn0000_0001_EBX:BMI1 != Bmi1.IsSupported");
                 testResult = Fail;
             }
 
-            (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)0x80000000), 0x00000000);
+            isHierarchyDisabled = isAvx2HierarchyDisabled | isFmaHierarchyDisabled | OperatingSystem.IsMacOS();
+
+            for (int i = 0; i < 2; i++)
+            {
+                // The runtime currently requires that all of F + BW + CD + DQ + VL be supported together or none
+                // are supported. To handle this we simple check them all twice so that if any of them are disabled
+                // the first time around, we'll then assert that they are all actually disabled the second time around
+
+                if (IsBitIncorrect(ebx, 16, typeof(Avx512F), Avx512F.IsSupported, "AVX512F", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 31, typeof(Avx512F.VL), Avx512F.VL.IsSupported, "AVX512F_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 30, typeof(Avx512BW), Avx512BW.IsSupported, "AVX512BW", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 30, typeof(Avx512BW.VL), Avx512BW.VL.IsSupported, "AVX512BW_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 28, typeof(Avx512CD), Avx512CD.IsSupported, "AVX512CD", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 28, typeof(Avx512CD.VL), Avx512CD.VL.IsSupported, "AVX512CD_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 17, typeof(Avx512DQ), Avx512DQ.IsSupported, "AVX512DQ", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 17, typeof(Avx512DQ.VL), Avx512DQ.VL.IsSupported, "AVX512DQ_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+            }
+
+            bool isAvx512HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 1, typeof(Avx512Vbmi), Avx512Vbmi.IsSupported, "AVX512VBMI", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsBitIncorrect(ecx, 1, typeof(Avx512Vbmi.VL), Avx512Vbmi.VL.IsSupported, "AVX512VBMI_VL", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            isHierarchyDisabled = isX86BaseDisabled;
 
-            if (isAuthenticAmd && ((ebx != 0x68747541) || (ecx != 0x444D4163) || (edx != 0x69746E65)))
+            if (IsBitIncorrect(edx, 14, typeof(X86Serialize), X86Serialize.IsSupported, "SERIALIZE", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn8000_0000 reported different vendor info from Fn0000_0000");
                 testResult = Fail;
             }
 
-            if (isGenuineIntel && ((ebx != 0x756E6547) && (ecx != 0x6C65746E) && (edx != 0x6C656E69)))
+            (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000007, 0x00000001);
+
+            isHierarchyDisabled = isAvx2HierarchyDisabled;
+
+#pragma warning disable CA2252 // No need to opt into preview feature for an internal test
+            if (IsBitIncorrect(eax, 4, typeof(AvxVnni), AvxVnni.IsSupported, "AVXVNNI", ref isHierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn8000_0000 reported different vendor info from Fn0000_0000");
                 testResult = Fail;
             }
+#pragma warning restore CA2252
+
+            (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)0x80000000), 0x00000000);
 
-            int maxFunctionIdEx = eax;
+            uint maxFunctionIdEx = (uint)eax;
 
             if (maxFunctionIdEx < 0x00000001)
             {
@@ -174,9 +253,63 @@ namespace IntelHardwareIntrinsicTest._CpuId
 
             (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)0x80000001), 0x00000000);
 
-            if (IsBitIncorrect(ecx, 5, Lzcnt.IsSupported, "LZCNT"))
+            isHierarchyDisabled = isX86BaseDisabled;
+
+            if (IsBitIncorrect(ecx, 5, typeof(Lzcnt), Lzcnt.IsSupported, "LZCNT", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector64), Vector64.IsHardwareAccelerated, isHierarchyDisabled: true))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector128), Vector128.IsHardwareAccelerated, isSse2HierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector256), Vector256.IsHardwareAccelerated, isAvx2HierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector512), Vector512.IsHardwareAccelerated, isAvx512HierarchyDisabled))
             {
-                Console.WriteLine("CPUID Fn8000_0001_ECX:LZCNT != Lzcnt.IsSupported");
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector), Vector.IsHardwareAccelerated, isSse2HierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (Vector<byte>.Count == 16)
+            {
+                if (!isAvx2HierarchyDisabled)
+                {
+                    Console.WriteLine($"{typeof(Vector).FullName}.Count returned 16 but the hardware returned 32");
+                    testResult = Fail;
+                }
+            }
+            else if (Vector<byte>.Count == 32)
+            {
+                if (isAvx2HierarchyDisabled)
+                {
+                    Console.WriteLine($"{typeof(Vector).FullName}.Count returned 32 but the hardware returned 16");
+                    testResult = Fail;
+                }
+            }
+            else
+            {
+                Console.WriteLine($"{typeof(Vector).FullName}.Count returned {Vector<byte>.Count} which is unexpected");
+                testResult = Fail;
+            }
+
+            if (Vector<byte>.Count != (int)typeof(Vector<byte>).GetProperty("Count")!.GetValue(null)!)
+            {
+                Console.WriteLine($"{typeof(Vector).FullName}.Count returned a different result when called via reflection");
                 testResult = Fail;
             }
 
@@ -184,10 +317,84 @@ namespace IntelHardwareIntrinsicTest._CpuId
             return;
         }
 
-        static bool IsBitIncorrect(int register, int bitNumber, bool expectedResult, string name)
+        static bool IsBitIncorrect(int register, int bitNumber, Type isa, bool isSupported, string name, ref bool isHierarchyDisabled)
         {
-            return ((register & (1 << bitNumber)) != ((expectedResult ? 1 : 0) << bitNumber))
-                && (Environment.GetEnvironmentVariable($"DOTNET_Enable{name}") is null);
+            bool isSupportedByHardware = (register & (1 << bitNumber)) != 0;
+            isHierarchyDisabled |= !GetDotnetEnable(name);
+
+            if (isSupported)
+            {
+                if (!isSupportedByHardware)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsSupported returned true but the hardware returned false");
+                    return true;
+                }
+
+                if (isHierarchyDisabled)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsSupported returned true but the runtime returned false");
+                    return true;
+                }
+            }
+            else if (isSupportedByHardware)
+            {
+                if (!isHierarchyDisabled)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsSupported returned false but the hardware and runtime returned true");
+                    return true;
+                }
+            }
+            else
+            {
+                // The IsSupported query returned false and the hardware
+                // says its unsupported, so we're all good
+            }
+
+            if (isSupported != (bool)isa.GetProperty("IsSupported")!.GetValue(null)!)
+            {
+                Console.WriteLine($"{isa.FullName}.IsSupported returned a different result when called via reflection");
+                return true;
+            }
+
+            return false;
+        }
+
+        static bool IsIncorrect(Type isa, bool isHardwareAccelerated, bool isHierarchyDisabled)
+        {
+            if (isHardwareAccelerated)
+            {
+                if (isHierarchyDisabled)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsHardwareAccelerated returned true but the runtime returned false");
+                    return true;
+                }
+            }
+            else if (!isHierarchyDisabled)
+            {
+                Console.WriteLine($"{isa.FullName}.IsHardwareAccelerated returned false but the hardware and runtime returned true");
+                return true;
+            }
+
+            if (isHardwareAccelerated != (bool)isa.GetProperty("IsHardwareAccelerated")!.GetValue(null)!)
+            {
+                Console.WriteLine($"{isa.FullName}.IsHardwareAccelerated returned a different result when called via reflection");
+                return true;
+            }
+
+            return false;
+        }
+
+        static bool GetDotnetEnable(string name)
+        {
+            string? stringValue = Environment.GetEnvironmentVariable($"DOTNET_Enable{name}");
+
+            if ((stringValue is null) || !int.TryParse(stringValue, out int value))
+            {
+                // Hardware Intrinsic configuration knobs default to true
+                return true;
+            }
+
+            return value != 0;
         }
     }
 }
index 06f6a02..d46d907 100644 (file)
@@ -50,46 +50,131 @@ unsafe class Program
         //
         // The test is compiled with multiple defines to test this.
 
+        bool ExpectedVectorsAccelerated = true;
+
+        bool? ExpectedSse = true;
+        bool? ExpectedSse2 = true;
+
 #if BASELINE_INTRINSICS
-        bool vectorsAccelerated = true;
-        int byteVectorLength = 16;
-        bool? Sse2AndBelow = true;
-        bool? Sse3Group = null;
-        bool? AesLzPcl = null;
-        bool? Sse4142 = null;
-        bool? PopCnt = null;
-        bool? Avx12 = false;
-        bool? FmaBmi12 = false;
-        bool? Avxvnni = false;
-#elif NON_VEX_INTRINSICS
-        bool vectorsAccelerated = true;
+        bool? ExpectedSse3 = null;
+        bool? ExpectedSsse3 = null;
+        bool? ExpectedAes = null;
+        bool? ExpectedLzcnt = null;
+        bool? ExpectedPclmulqdq = null;
+        bool? ExpectedSse41 = null;
+        bool? ExpectedSse42 = null;
+        bool? ExpectedPopcnt = null;
+        bool? ExpectedAvx = false;
+        bool? ExpectedAvx2 = false;
+        bool? ExpectedFma = false;
+        bool? ExpectedBmi1 = false;
+        bool? ExpectedBmi2 = false;
+        bool? ExpectedAvxVnni = false;
+        bool? ExpectedAvx512F = false;
+        bool? ExpectedAvx512BW = false;
+        bool? ExpectedAvx512CD = false;
+        bool? ExpectedAvx512DQ = false;
+        bool? ExpectedAvx512Vbmi = false;
+        bool? ExpectedX86Serialize = null;
+#elif SSE42_INTRINSICS
+        bool? ExpectedSse3 = true;
+        bool? ExpectedSsse3 = true;
+        bool? ExpectedAes = null;
+        bool? ExpectedLzcnt = null;
+        bool? ExpectedPclmulqdq = null;
+        bool? ExpectedSse41 = true;
+        bool? ExpectedSse42 = true;
+        bool? ExpectedPopcnt = null;
+        bool? ExpectedAvx = false;
+        bool? ExpectedAvx2 = false;
+        bool? ExpectedFma = false;
+        bool? ExpectedBmi1 = false;
+        bool? ExpectedBmi2 = false;
+        bool? ExpectedAvxVnni = false;
+        bool? ExpectedAvx512F = false;
+        bool? ExpectedAvx512BW = false;
+        bool? ExpectedAvx512CD = false;
+        bool? ExpectedAvx512DQ = false;
+        bool? ExpectedAvx512Vbmi = false;
+        bool? ExpectedX86Serialize = null;
+#elif AVX_INTRINSICS
+        bool? ExpectedSse3 = true;
+        bool? ExpectedSsse3 = true;
+        bool? ExpectedAes = null;
+        bool? ExpectedLzcnt = null;
+        bool? ExpectedPclmulqdq = null;
+        bool? ExpectedSse41 = true;
+        bool? ExpectedSse42 = true;
+        bool? ExpectedPopcnt = null;
+        bool? ExpectedAvx = true;
+        bool? ExpectedAvx2 = false; // TODO: Fix once opportunistic Avx2 is allowed
+        bool? ExpectedFma = null;
+        bool? ExpectedBmi1 = null;
+        bool? ExpectedBmi2 = null;
+        bool? ExpectedAvxVnni = false; // TODO: Fix once opportunistic Avx2 is allowed
+        bool? ExpectedAvx512F = false;
+        bool? ExpectedAvx512BW = false;
+        bool? ExpectedAvx512CD = false;
+        bool? ExpectedAvx512DQ = false;
+        bool? ExpectedAvx512Vbmi = false;
+        bool? ExpectedX86Serialize = null;
+#elif AVX2_INTRINSICS
+        bool? ExpectedSse3 = true;
+        bool? ExpectedSsse3 = true;
+        bool? ExpectedAes = null;
+        bool? ExpectedLzcnt = null;
+        bool? ExpectedPclmulqdq = null;
+        bool? ExpectedSse41 = true;
+        bool? ExpectedSse42 = true;
+        bool? ExpectedPopcnt = null;
+        bool? ExpectedAvx = true;
+        bool? ExpectedAvx2 = true;
+        bool? ExpectedFma = null;
+        bool? ExpectedBmi1 = null;
+        bool? ExpectedBmi2 = null;
+        bool? ExpectedAvxVnni = null;
+        bool? ExpectedAvx512F = false;
+        bool? ExpectedAvx512BW = false;
+        bool? ExpectedAvx512CD = false;
+        bool? ExpectedAvx512DQ = false;
+        bool? ExpectedAvx512Vbmi = false;
+        bool? ExpectedX86Serialize = null;
+#elif AVX512_INTRINSICS
+        bool? ExpectedSse3 = true;
+        bool? ExpectedSsse3 = true;
+        bool? ExpectedAes = null;
+        bool? ExpectedLzcnt = null;
+        bool? ExpectedPclmulqdq = null;
+        bool? ExpectedSse41 = true;
+        bool? ExpectedSse42 = true;
+        bool? ExpectedPopcnt = null;
+        bool? ExpectedAvx = true;
+        bool? ExpectedAvx2 = true;
+        bool? ExpectedFma = true;
+        bool? ExpectedBmi1 = null;
+        bool? ExpectedBmi2 = null;
+        bool? ExpectedAvxVnni = null;
+        bool? ExpectedAvx512F = true;
+        bool? ExpectedAvx512BW = true;
+        bool? ExpectedAvx512CD = true;
+        bool? ExpectedAvx512DQ = true;
+        bool? ExpectedAvx512Vbmi = null;
+        bool? ExpectedX86Serialize = null;
+#else
+#error Who dis?
+#endif
+
+#if VECTORT128_INTRINSICS
         int byteVectorLength = 16;
-        bool? Sse2AndBelow = true;
-        bool? Sse3Group = true;
-        bool? AesLzPcl = null;
-        bool? Sse4142 = true;
-        bool? PopCnt = null;
-        bool? Avx12 = false;
-        bool? FmaBmi12 = false;
-        bool? Avxvnni = false;
-#elif VEX_INTRINSICS
-        bool vectorsAccelerated = true;
+#elif VECTORT256_INTRINSICS
         int byteVectorLength = 32;
-        bool? Sse2AndBelow = true;
-        bool? Sse3Group = true;
-        bool? AesLzPcl = null;
-        bool? Sse4142 = true;
-        bool? PopCnt = null;
-        bool? Avx12 = true;
-        bool? FmaBmi12 = null;
-        bool? Avxvnni = null;
 #else
 #error Who dis?
 #endif
 
-        if (vectorsAccelerated != Vector.IsHardwareAccelerated)
+        if (ExpectedVectorsAccelerated != Vector.IsHardwareAccelerated)
         {
-            throw new Exception($"Vectors HW acceleration state unexpected - expected {vectorsAccelerated}, got {Vector.IsHardwareAccelerated}");
+            throw new Exception($"Vectors HW acceleration state unexpected - expected {ExpectedVectorsAccelerated}, got {Vector.IsHardwareAccelerated}");
         }
 
         if (byteVectorLength != Vector<byte>.Count)
@@ -97,53 +182,76 @@ unsafe class Program
             throw new Exception($"Unexpected vector length - expected {byteVectorLength}, got {Vector<byte>.Count}");
         }
 
-        Check("Sse", Sse2AndBelow, &SseIsSupported, Sse.IsSupported, () => Sse.Subtract(Vector128<float>.Zero, Vector128<float>.Zero).Equals(Vector128<float>.Zero));
-        Check("Sse.X64", Sse2AndBelow, &SseX64IsSupported, Sse.X64.IsSupported, () => Sse.X64.ConvertToInt64WithTruncation(Vector128<float>.Zero) == 0);
+        Check("Sse", ExpectedSse, &SseIsSupported, Sse.IsSupported, () => Sse.Subtract(Vector128<float>.Zero, Vector128<float>.Zero).Equals(Vector128<float>.Zero));
+        Check("Sse.X64", ExpectedSse, &SseX64IsSupported, Sse.X64.IsSupported, () => Sse.X64.ConvertToInt64WithTruncation(Vector128<float>.Zero) == 0);
+
+        Check("Sse2", ExpectedSse2, &Sse2IsSupported, Sse2.IsSupported, () => Sse2.Extract(Vector128<ushort>.Zero, 0) == 0);
+        Check("Sse2.X64", ExpectedSse2, &Sse2X64IsSupported, Sse2.X64.IsSupported, () => Sse2.X64.ConvertToInt64(Vector128<double>.Zero) == 0);
+
+        Check("Sse3", ExpectedSse3, &Sse3IsSupported, Sse3.IsSupported, () => Sse3.MoveHighAndDuplicate(Vector128<float>.Zero).Equals(Vector128<float>.Zero));
+        Check("Sse3.X64", ExpectedSse3, &Sse3X64IsSupported, Sse3.X64.IsSupported, null);
+
+        Check("Ssse3", ExpectedSsse3, &Ssse3IsSupported, Ssse3.IsSupported, () => Ssse3.Abs(Vector128<short>.Zero).Equals(Vector128<ushort>.Zero));
+        Check("Ssse3.X64", ExpectedSsse3, &Ssse3X64IsSupported, Ssse3.X64.IsSupported, null);
+
+        Check("Sse41", ExpectedSse41, &Sse41IsSupported, Sse41.IsSupported, () => Sse41.Max(Vector128<int>.Zero, Vector128<int>.Zero).Equals(Vector128<int>.Zero));
+        Check("Sse41.X64", ExpectedSse41, &Sse41X64IsSupported, Sse41.X64.IsSupported, () => Sse41.X64.Extract(Vector128<long>.Zero, 0) == 0);
+
+        Check("Sse42", ExpectedSse42, &Sse42IsSupported, Sse42.IsSupported, () => Sse42.Crc32(0, 0) == 0);
+        Check("Sse42.X64", ExpectedSse42, &Sse42X64IsSupported, Sse42.X64.IsSupported, () => Sse42.X64.Crc32(0, 0) == 0);
+
+        Check("Aes", ExpectedAes, &AesIsSupported, Aes.IsSupported, () => Aes.KeygenAssist(Vector128<byte>.Zero, 0).Equals(Vector128.Create((byte)99)));
+        Check("Aes.X64", ExpectedAes, &AesX64IsSupported, Aes.X64.IsSupported, null);
 
-        Check("Sse2", Sse2AndBelow, &Sse2IsSupported, Sse2.IsSupported, () => Sse2.Extract(Vector128<ushort>.Zero, 0) == 0);
-        Check("Sse2.X64", Sse2AndBelow, &Sse2X64IsSupported, Sse2.X64.IsSupported, () => Sse2.X64.ConvertToInt64(Vector128<double>.Zero) == 0);
+        Check("Avx", ExpectedAvx, &AvxIsSupported, Avx.IsSupported, () => Avx.Add(Vector256<double>.Zero, Vector256<double>.Zero).Equals(Vector256<double>.Zero));
+        Check("Avx.X64", ExpectedAvx, &AvxX64IsSupported, Avx.X64.IsSupported, null);
 
-        Check("Sse3", Sse3Group, &Sse3IsSupported, Sse3.IsSupported, () => Sse3.MoveHighAndDuplicate(Vector128<float>.Zero).Equals(Vector128<float>.Zero));
-        Check("Sse3.X64", Sse3Group, &Sse3X64IsSupported, Sse3.X64.IsSupported, null);
+        Check("Avx2", ExpectedAvx2, &Avx2IsSupported, Avx2.IsSupported, () => Avx2.Abs(Vector256<int>.Zero).Equals(Vector256<uint>.Zero));
+        Check("Avx2.X64", ExpectedAvx2, &Avx2X64IsSupported, Avx2.X64.IsSupported, null);
 
-        Check("Ssse3", Sse3Group, &Ssse3IsSupported, Ssse3.IsSupported, () => Ssse3.Abs(Vector128<short>.Zero).Equals(Vector128<ushort>.Zero));
-        Check("Ssse3.X64", Sse3Group, &Ssse3X64IsSupported, Ssse3.X64.IsSupported, null);
+        Check("Bmi1", ExpectedBmi1, &Bmi1IsSupported, Bmi1.IsSupported, () => Bmi1.AndNot(0, 0) == 0);
+        Check("Bmi1.X64", ExpectedBmi1, &Bmi1X64IsSupported, Bmi1.X64.IsSupported, () => Bmi1.X64.AndNot(0, 0) == 0);
 
-        Check("Sse41", Sse4142, &Sse41IsSupported, Sse41.IsSupported, () => Sse41.Max(Vector128<int>.Zero, Vector128<int>.Zero).Equals(Vector128<int>.Zero));
-        Check("Sse41.X64", Sse4142, &Sse41X64IsSupported, Sse41.X64.IsSupported, () => Sse41.X64.Extract(Vector128<long>.Zero, 0) == 0);
+        Check("Bmi2", ExpectedBmi2, &Bmi2IsSupported, Bmi2.IsSupported, () => Bmi2.MultiplyNoFlags(0, 0) == 0);
+        Check("Bmi2.X64", ExpectedBmi2, &Bmi2X64IsSupported, Bmi2.X64.IsSupported, () => Bmi2.X64.MultiplyNoFlags(0, 0) == 0);
 
-        Check("Sse42", Sse4142, &Sse42IsSupported, Sse42.IsSupported, () => Sse42.Crc32(0, 0) == 0);
-        Check("Sse42.X64", Sse4142, &Sse42X64IsSupported, Sse42.X64.IsSupported, () => Sse42.X64.Crc32(0, 0) == 0);
+        Check("Fma", ExpectedFma, &FmaIsSupported, Fma.IsSupported, () => Fma.MultiplyAdd(Vector128<float>.Zero, Vector128<float>.Zero, Vector128<float>.Zero).Equals(Vector128<float>.Zero));
+        Check("Fma.X64", ExpectedFma, &FmaX64IsSupported, Fma.X64.IsSupported, null);
 
-        Check("Aes", AesLzPcl, &AesIsSupported, Aes.IsSupported, () => Aes.KeygenAssist(Vector128<byte>.Zero, 0).Equals(Vector128.Create((byte)99)));
-        Check("Aes.X64", AesLzPcl, &AesX64IsSupported, Aes.X64.IsSupported, null);
+        Check("Lzcnt", ExpectedLzcnt, &LzcntIsSupported, Lzcnt.IsSupported, () => Lzcnt.LeadingZeroCount(0) == 32);
+        Check("Lzcnt.X64", ExpectedLzcnt, &LzcntX64IsSupported, Lzcnt.X64.IsSupported, () => Lzcnt.X64.LeadingZeroCount(0) == 64);
 
-        Check("Avx", Avx12, &AvxIsSupported, Avx.IsSupported, () => Avx.Add(Vector256<double>.Zero, Vector256<double>.Zero).Equals(Vector256<double>.Zero));
-        Check("Avx.X64", Avx12, &AvxX64IsSupported, Avx.X64.IsSupported, null);
+        Check("Pclmulqdq", ExpectedPclmulqdq, &PclmulqdqIsSupported, Pclmulqdq.IsSupported, () => Pclmulqdq.CarrylessMultiply(Vector128<long>.Zero, Vector128<long>.Zero, 0).Equals(Vector128<long>.Zero));
+        Check("Pclmulqdq.X64", ExpectedPclmulqdq, &PclmulqdqX64IsSupported, Pclmulqdq.X64.IsSupported, null);
 
-        Check("Avx2", Avx12, &Avx2IsSupported, Avx2.IsSupported, () => Avx2.Abs(Vector256<int>.Zero).Equals(Vector256<uint>.Zero));
-        Check("Avx2.X64", Avx12, &Avx2X64IsSupported, Avx2.X64.IsSupported, null);
+        Check("Popcnt", ExpectedPopcnt, &PopcntIsSupported, Popcnt.IsSupported, () => Popcnt.PopCount(0) == 0);
+        Check("Popcnt.X64", ExpectedPopcnt, &PopcntX64IsSupported, Popcnt.X64.IsSupported, () => Popcnt.X64.PopCount(0) == 0);
 
-        Check("Bmi1", FmaBmi12, &Bmi1IsSupported, Bmi1.IsSupported, () => Bmi1.AndNot(0, 0) == 0);
-        Check("Bmi1.X64", FmaBmi12, &Bmi1X64IsSupported, Bmi1.X64.IsSupported, () => Bmi1.X64.AndNot(0, 0) == 0);
+        Check("AvxVnni", ExpectedAvxVnni, &AvxVnniIsSupported, AvxVnni.IsSupported, () => AvxVnni.MultiplyWideningAndAdd(Vector128<int>.Zero, Vector128<byte>.Zero, Vector128<sbyte>.Zero).Equals(Vector128<int>.Zero));
+        Check("AvxVnni.X64", ExpectedAvxVnni, &AvxVnniX64IsSupported, AvxVnni.X64.IsSupported, null);
 
-        Check("Bmi2", FmaBmi12, &Bmi2IsSupported, Bmi2.IsSupported, () => Bmi2.MultiplyNoFlags(0, 0) == 0);
-        Check("Bmi2.X64", FmaBmi12, &Bmi2X64IsSupported, Bmi2.X64.IsSupported, () => Bmi2.X64.MultiplyNoFlags(0, 0) == 0);
+        Check("Avx512F", ExpectedAvx512F, &Avx512FIsSupported, Avx512F.IsSupported, () => Avx512F.Abs(Vector512<int>.Zero).Equals(Vector512<uint>.Zero));
+        Check("Avx512F.VL", ExpectedAvx512F, &Avx512FVLIsSupported, Avx512F.VL.IsSupported, null);
+        Check("Avx512F.X64", ExpectedAvx512F, &Avx512FX64IsSupported, Avx512F.X64.IsSupported, null);
 
-        Check("Fma", FmaBmi12, &FmaIsSupported, Fma.IsSupported, () => Fma.MultiplyAdd(Vector128<float>.Zero, Vector128<float>.Zero, Vector128<float>.Zero).Equals(Vector128<float>.Zero));
-        Check("Fma.X64", FmaBmi12, &FmaX64IsSupported, Fma.X64.IsSupported, null);
+        Check("Avx512BW", ExpectedAvx512BW, &Avx512BWIsSupported, Avx512BW.IsSupported, () => Avx512BW.Abs(Vector512<sbyte>.Zero).Equals(Vector512<byte>.Zero));
+        Check("Avx512BW.VL", ExpectedAvx512BW, &Avx512BWVLIsSupported, Avx512BW.VL.IsSupported, null);
+        Check("Avx512BW.X64", ExpectedAvx512BW, &Avx512BWX64IsSupported, Avx512BW.X64.IsSupported, null);
 
-        Check("Lzcnt", AesLzPcl, &LzcntIsSupported, Lzcnt.IsSupported, () => Lzcnt.LeadingZeroCount(0) == 32);
-        Check("Lzcnt.X64", AesLzPcl, &LzcntX64IsSupported, Lzcnt.X64.IsSupported, () => Lzcnt.X64.LeadingZeroCount(0) == 64);
+        Check("Avx512CD", ExpectedAvx512CD, &Avx512CDIsSupported, Avx512CD.IsSupported, () => Avx512CD.LeadingZeroCount(Vector512<uint>.AllBitsSet) == Vector512<uint>.Zero);
+        Check("Avx512CD.VL", ExpectedAvx512CD, &Avx512CDVLIsSupported, Avx512CD.VL.IsSupported, null);
+        Check("Avx512CD.X64", ExpectedAvx512CD, &Avx512CDX64IsSupported, Avx512CD.X64.IsSupported, null);
 
-        Check("Pclmulqdq", AesLzPcl, &PclmulqdqIsSupported, Pclmulqdq.IsSupported, () => Pclmulqdq.CarrylessMultiply(Vector128<long>.Zero, Vector128<long>.Zero, 0).Equals(Vector128<long>.Zero));
-        Check("Pclmulqdq.X64", AesLzPcl, &PclmulqdqX64IsSupported, Pclmulqdq.X64.IsSupported, null);
+        Check("Avx512DQ", ExpectedAvx512DQ, &Avx512DQIsSupported, Avx512DQ.IsSupported, () => Avx512DQ.And(Vector512<float>.Zero, Vector512<float>.Zero).Equals(Vector512<float>.Zero));
+        Check("Avx512DQ.VL", ExpectedAvx512DQ, &Avx512DQVLIsSupported, Avx512DQ.VL.IsSupported, null);
+        Check("Avx512DQ.X64", ExpectedAvx512DQ, &Avx512DQX64IsSupported, Avx512DQ.X64.IsSupported, null);
 
-        Check("Popcnt", PopCnt, &PopcntIsSupported, Popcnt.IsSupported, () => Popcnt.PopCount(0) == 0);
-        Check("Popcnt.X64", PopCnt, &PopcntX64IsSupported, Popcnt.X64.IsSupported, () => Popcnt.X64.PopCount(0) == 0);
+        Check("Avx512Vbmi", ExpectedAvx512Vbmi, &Avx512VbmiIsSupported, Avx512Vbmi.IsSupported, () => Avx512Vbmi.PermuteVar64x8(Vector512<sbyte>.Zero, Vector512<sbyte>.Zero).Equals(Vector512<sbyte>.Zero));
+        Check("Avx512Vbmi.VL", ExpectedAvx512Vbmi, &Avx512VbmiVLIsSupported, Avx512Vbmi.VL.IsSupported, null);
+        Check("Avx512Vbmi.X64", ExpectedAvx512Vbmi, &Avx512VbmiX64IsSupported, Avx512Vbmi.X64.IsSupported, null);
 
-        Check("AvxVnni", Avxvnni, &AvxVnniIsSupported, AvxVnni.IsSupported, () => AvxVnni.MultiplyWideningAndAdd(Vector128<int>.Zero, Vector128<byte>.Zero, Vector128<sbyte>.Zero).Equals(Vector128<int>.Zero));
-        Check("AvxVnni.X64", Avxvnni, &AvxVnniX64IsSupported, AvxVnni.X64.IsSupported, null);
+        Check("X86Serialize", ExpectedX86Serialize, &X86SerializeIsSupported, X86Serialize.IsSupported, () => { X86Serialize.Serialize(); return true; } );
+        Check("X86Serialize.X64", ExpectedX86Serialize, &X86SerializeX64IsSupported, X86Serialize.X64.IsSupported, null);
 
         return s_success ? 100 : 1;
     }
@@ -183,6 +291,23 @@ unsafe class Program
     static bool PopcntX64IsSupported() => Popcnt.X64.IsSupported;
     static bool AvxVnniIsSupported() => AvxVnni.IsSupported;
     static bool AvxVnniX64IsSupported() => AvxVnni.X64.IsSupported;
+    static bool Avx512FIsSupported() => Avx512F.IsSupported;
+    static bool Avx512FVLIsSupported() => Avx512F.VL.IsSupported;
+    static bool Avx512FX64IsSupported() => Avx512F.X64.IsSupported;
+    static bool Avx512BWIsSupported() => Avx512BW.IsSupported;
+    static bool Avx512BWVLIsSupported() => Avx512BW.VL.IsSupported;
+    static bool Avx512BWX64IsSupported() => Avx512BW.X64.IsSupported;
+    static bool Avx512CDIsSupported() => Avx512CD.IsSupported;
+    static bool Avx512CDVLIsSupported() => Avx512CD.VL.IsSupported;
+    static bool Avx512CDX64IsSupported() => Avx512CD.X64.IsSupported;
+    static bool Avx512DQIsSupported() => Avx512DQ.IsSupported;
+    static bool Avx512DQVLIsSupported() => Avx512DQ.VL.IsSupported;
+    static bool Avx512DQX64IsSupported() => Avx512DQ.X64.IsSupported;
+    static bool Avx512VbmiIsSupported() => Avx512Vbmi.IsSupported;
+    static bool Avx512VbmiVLIsSupported() => Avx512Vbmi.VL.IsSupported;
+    static bool Avx512VbmiX64IsSupported() => Avx512Vbmi.X64.IsSupported;
+    static bool X86SerializeIsSupported() => X86Serialize.IsSupported;
+    static bool X86SerializeX64IsSupported() => X86Serialize.X64.IsSupported;
 
     static bool IsConstantTrue(delegate*<bool> code)
     {
diff --git a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx.csproj b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx.csproj
new file mode 100644 (file)
index 0000000..3cf9043
--- /dev/null
@@ -0,0 +1,38 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+    <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'x64'">true</CLRTestTargetUnsupported>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DefineConstants>$(DefineConstants);AVX_INTRINSICS;VECTORT128_INTRINSICS</DefineConstants>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <IlcArg Include="--instruction-set:avx" />
+  </ItemGroup>
+
+  <PropertyGroup>
+    <CLRTestBashPreCommands><![CDATA[
+$(CLRTestBashPreCommands)
+    if [[ "$OSTYPE" == "darwin"* ]]; then
+        sysctl -a
+        sysctl -a | grep machdep.cpu.leaf7_features | grep AVX >/dev/null
+        if [ $? -ne 0 ]; then
+          echo No support for AVX, test not applicable.
+          exit 0
+        fi
+    fi
+    if [[ "$OSTYPE" == "linux"* ]]; then
+        if ! grep -q '^flags.*avx' /proc/cpuinfo 2>/dev/null; then
+          echo No support for AVX, test not applicable.
+          exit 0
+        fi
+    fi
+]]></CLRTestBashPreCommands>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+</Project>
@@ -5,7 +5,7 @@
     <CLRTestPriority>0</CLRTestPriority>
     <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'x64'">true</CLRTestTargetUnsupported>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <DefineConstants>$(DefineConstants);VEX_INTRINSICS</DefineConstants>
+    <DefineConstants>$(DefineConstants);AVX2_INTRINSICS;VECTORT256_INTRINSICS</DefineConstants>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx512.csproj b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx512.csproj
new file mode 100644 (file)
index 0000000..24da060
--- /dev/null
@@ -0,0 +1,30 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+    <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'x64' OR '$(TargetsOSX)' == 'true'">true</CLRTestTargetUnsupported>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DefineConstants>$(DefineConstants);AVX512_INTRINSICS;VECTORT256_INTRINSICS</DefineConstants>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <IlcArg Include="--instruction-set:avx512f,avx512f_vl,avx512bw,avx512bw_vl,avx512cd,avx512cd_vl,avx512dq,avx512dq_vl" />
+  </ItemGroup>
+
+  <PropertyGroup>
+    <CLRTestBashPreCommands><![CDATA[
+$(CLRTestBashPreCommands)
+    if [[ "$OSTYPE" == "linux"* ]]; then
+        if ! grep -q '^flags.*avx512' /proc/cpuinfo 2>/dev/null; then
+          echo No support for AVX512, test not applicable.
+          exit 0
+        fi
+    fi
+]]></CLRTestBashPreCommands>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx_NoAvx2.csproj b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/X64Avx_NoAvx2.csproj
new file mode 100644 (file)
index 0000000..9565259
--- /dev/null
@@ -0,0 +1,38 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+    <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'x64'">true</CLRTestTargetUnsupported>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DefineConstants>$(DefineConstants);AVX_INTRINSICS;VECTORT128_INTRINSICS</DefineConstants>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <IlcArg Include="--instruction-set:avx,-avx2" />
+  </ItemGroup>
+
+  <PropertyGroup>
+    <CLRTestBashPreCommands><![CDATA[
+$(CLRTestBashPreCommands)
+    if [[ "$OSTYPE" == "darwin"* ]]; then
+        sysctl -a
+        sysctl -a | grep machdep.cpu.leaf7_features | grep AVX >/dev/null
+        if [ $? -ne 0 ]; then
+          echo No support for AVX, test not applicable.
+          exit 0
+        fi
+    fi
+    if [[ "$OSTYPE" == "linux"* ]]; then
+        if ! grep -q '^flags.*avx' /proc/cpuinfo 2>/dev/null; then
+          echo No support for AVX, test not applicable.
+          exit 0
+        fi
+    fi
+]]></CLRTestBashPreCommands>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+</Project>
index e49eb84..9e5d0c7 100644 (file)
@@ -5,7 +5,7 @@
     <CLRTestPriority>0</CLRTestPriority>
     <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'x64'">true</CLRTestTargetUnsupported>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <DefineConstants>$(DefineConstants);BASELINE_INTRINSICS</DefineConstants>
+    <DefineConstants>$(DefineConstants);BASELINE_INTRINSICS;VECTORT128_INTRINSICS</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
@@ -5,7 +5,7 @@
     <CLRTestPriority>0</CLRTestPriority>
     <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'x64'">true</CLRTestTargetUnsupported>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <DefineConstants>$(DefineConstants);NON_VEX_INTRINSICS</DefineConstants>
+    <DefineConstants>$(DefineConstants);SSE42_INTRINSICS;VECTORT128_INTRINSICS</DefineConstants>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/tests/readytorun/HardwareIntrinsics/X86/CpuId.cs b/src/tests/readytorun/HardwareIntrinsics/X86/CpuId.cs
new file mode 100644 (file)
index 0000000..38c22a6
--- /dev/null
@@ -0,0 +1,393 @@
+// 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.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.X86;
+using System.Runtime.Intrinsics;
+using System.Reflection;
+
+namespace IntelHardwareIntrinsicTest._CpuId
+{
+    public class Program
+    {
+        const int Pass = 100;
+        const int Fail = 0;
+
+        public unsafe static int Main()
+        {
+            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);
+            bool isVirtualCPU = (ebx == 0x74726956) && (ecx == 0x20555043) && (edx == 0x206C6175);
+
+            if (!isAuthenticAmd && !isGenuineIntel && !isVirtualCPU)
+            {
+                // CPUID checks are vendor specific and aren't guaranteed to match up, even across Intel/AMD
+                // as such, we limit ourselves to just AuthenticAMD, GenuineIntel and "Virtual CPU" 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;
+            }
+
+            uint maxFunctionId = (uint)eax;
+
+            if (maxFunctionId < 0x00000001)
+            {
+                return testResult;
+            }
+
+            bool isX86BaseDisabled = !GetDotnetEnable("HWINTRINSIC");
+            bool isHierarchyDisabled = isX86BaseDisabled;
+
+            (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000001, 0x00000000);
+
+            if (IsBitIncorrect(edx, 25, typeof(Sse), Sse.IsSupported, "SSE", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsBitIncorrect(edx, 26, typeof(Sse2), Sse2.IsSupported, "SSE2", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            bool isSse2HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 25, typeof(Aes), Aes.IsSupported, "AES", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            isHierarchyDisabled = isSse2HierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 1, typeof(Pclmulqdq), Pclmulqdq.IsSupported, "PCLMULQDQ", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            isHierarchyDisabled = isSse2HierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 0, typeof(Sse3), Sse3.IsSupported, "SSE3", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsBitIncorrect(ecx, 9, typeof(Ssse3), Ssse3.IsSupported, "SSSE3", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsBitIncorrect(ecx, 19, typeof(Sse41), Sse41.IsSupported, "SSE41", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsBitIncorrect(ecx, 20, typeof(Sse42), Sse42.IsSupported, "SSE42", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            bool isSse42HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 23, typeof(Popcnt), Popcnt.IsSupported, "POPCNT", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            isHierarchyDisabled = isSse42HierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 28, typeof(Avx), Avx.IsSupported, "AVX", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            bool isAvxHierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 12, typeof(Fma), Fma.IsSupported, "FMA", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            bool isFmaHierarchyDisabled = isHierarchyDisabled;
+
+            if (maxFunctionId < 0x00000007)
+            {
+                return testResult;
+            }
+
+            (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000007, 0x00000000);
+
+            isHierarchyDisabled = isAvxHierarchyDisabled;
+
+            if (IsBitIncorrect(ebx, 5, typeof(Avx2), Avx2.IsSupported, "AVX2", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            bool isAvx2HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ebx, 3, typeof(Bmi1), Bmi1.IsSupported, "BMI1", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            isHierarchyDisabled = isAvx2HierarchyDisabled;
+
+            if (IsBitIncorrect(ebx, 8, typeof(Bmi2), Bmi2.IsSupported, "BMI2", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            isHierarchyDisabled = isAvx2HierarchyDisabled | isFmaHierarchyDisabled | OperatingSystem.IsMacOS();
+
+            for (int i = 0; i < 2; i++)
+            {
+                // The runtime currently requires that all of F + BW + CD + DQ + VL be supported together or none
+                // are supported. To handle this we simple check them all twice so that if any of them are disabled
+                // the first time around, we'll then assert that they are all actually disabled the second time around
+
+                if (IsBitIncorrect(ebx, 16, typeof(Avx512F), Avx512F.IsSupported, "AVX512F", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 31, typeof(Avx512F.VL), Avx512F.VL.IsSupported, "AVX512F_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 30, typeof(Avx512BW), Avx512BW.IsSupported, "AVX512BW", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 30, typeof(Avx512BW.VL), Avx512BW.VL.IsSupported, "AVX512BW_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 28, typeof(Avx512CD), Avx512CD.IsSupported, "AVX512CD", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 28, typeof(Avx512CD.VL), Avx512CD.VL.IsSupported, "AVX512CD_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 17, typeof(Avx512DQ), Avx512DQ.IsSupported, "AVX512DQ", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+
+                if (IsBitIncorrect(ebx, 17, typeof(Avx512DQ.VL), Avx512DQ.VL.IsSupported, "AVX512DQ_VL", ref isHierarchyDisabled))
+                {
+                    testResult = Fail;
+                }
+            }
+
+            bool isAvx512HierarchyDisabled = isHierarchyDisabled;
+
+            if (IsBitIncorrect(ecx, 1, typeof(Avx512Vbmi), Avx512Vbmi.IsSupported, "AVX512VBMI", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsBitIncorrect(ecx, 1, typeof(Avx512Vbmi.VL), Avx512Vbmi.VL.IsSupported, "AVX512VBMI_VL", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            isHierarchyDisabled = isX86BaseDisabled;
+
+            if (IsBitIncorrect(edx, 14, typeof(X86Serialize), X86Serialize.IsSupported, "SERIALIZE", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            (eax, ebx, ecx, edx) = X86Base.CpuId(0x00000007, 0x00000001);
+
+            isHierarchyDisabled = isAvx2HierarchyDisabled;
+
+#pragma warning disable CA2252 // No need to opt into preview feature for an internal test
+            if (IsBitIncorrect(eax, 4, typeof(AvxVnni), AvxVnni.IsSupported, "AVXVNNI", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+#pragma warning restore CA2252
+
+            (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)0x80000000), 0x00000000);
+
+            uint maxFunctionIdEx = (uint)eax;
+
+            if (maxFunctionIdEx < 0x00000001)
+            {
+                return testResult;
+            }
+
+            (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)0x80000001), 0x00000000);
+
+            isHierarchyDisabled = isX86BaseDisabled;
+
+            if (IsBitIncorrect(ecx, 5, typeof(Lzcnt), Lzcnt.IsSupported, "LZCNT", ref isHierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector64), Vector64.IsHardwareAccelerated, isHierarchyDisabled: true))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector128), Vector128.IsHardwareAccelerated, isSse2HierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector256), Vector256.IsHardwareAccelerated, isAvx2HierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector512), Vector512.IsHardwareAccelerated, isAvx512HierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (IsIncorrect(typeof(Vector), Vector.IsHardwareAccelerated, isSse2HierarchyDisabled))
+            {
+                testResult = Fail;
+            }
+
+            if (Vector<byte>.Count == 16)
+            {
+                if (!isAvx2HierarchyDisabled)
+                {
+                    Console.WriteLine($"{typeof(Vector).FullName}.Count returned 16 but the hardware returned 32");
+                    testResult = Fail;
+                }
+            }
+            else if (Vector<byte>.Count == 32)
+            {
+                if (isAvx2HierarchyDisabled)
+                {
+                    Console.WriteLine($"{typeof(Vector).FullName}.Count returned 32 but the hardware returned 16");
+                    testResult = Fail;
+                }
+            }
+            else
+            {
+                Console.WriteLine($"{typeof(Vector).FullName}.Count returned {Vector<byte>.Count} which is unexpected");
+                testResult = Fail;
+            }
+
+            if (Vector<byte>.Count != (int)typeof(Vector<byte>).GetProperty("Count")!.GetValue(null)!)
+            {
+                Console.WriteLine($"{typeof(Vector).FullName}.Count returned a different result when called via reflection");
+                testResult = Fail;
+            }
+
+            return testResult;
+        }
+
+        static bool IsBitIncorrect(int register, int bitNumber, Type isa, bool isSupported, string name, ref bool isHierarchyDisabled)
+        {
+            bool isSupportedByHardware = (register & (1 << bitNumber)) != 0;
+            isHierarchyDisabled |= !GetDotnetEnable(name);
+
+            if (isSupported)
+            {
+                if (!isSupportedByHardware)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsSupported returned true but the hardware returned false");
+                    return true;
+                }
+
+                if (isHierarchyDisabled)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsSupported returned true but the runtime returned false");
+                    return true;
+                }
+            }
+            else if (isSupportedByHardware)
+            {
+                if (!isHierarchyDisabled)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsSupported returned false but the hardware and runtime returned true");
+                    return true;
+                }
+            }
+            else
+            {
+                // The IsSupported query returned false and the hardware
+                // says its unsupported, so we're all good
+            }
+
+            if (isSupported != (bool)isa.GetProperty("IsSupported")!.GetValue(null)!)
+            {
+                Console.WriteLine($"{isa.FullName}.IsSupported returned a different result when called via reflection");
+                return true;
+            }
+
+            return false;
+        }
+
+        static bool IsIncorrect(Type isa, bool isHardwareAccelerated, bool isHierarchyDisabled)
+        {
+            if (isHardwareAccelerated)
+            {
+                if (isHierarchyDisabled)
+                {
+                    Console.WriteLine($"{isa.FullName}.IsHardwareAccelerated returned true but the runtime returned false");
+                    return true;
+                }
+            }
+            else if (!isHierarchyDisabled)
+            {
+                Console.WriteLine($"{isa.FullName}.IsHardwareAccelerated returned false but the hardware and runtime returned true");
+                return true;
+            }
+
+            if (isHardwareAccelerated != (bool)isa.GetProperty("IsHardwareAccelerated")!.GetValue(null)!)
+            {
+                Console.WriteLine($"{isa.FullName}.IsHardwareAccelerated returned a different result when called via reflection");
+                return true;
+            }
+
+            return false;
+        }
+
+        static bool GetDotnetEnable(string name)
+        {
+            string? stringValue = Environment.GetEnvironmentVariable($"DOTNET_Enable{name}");
+
+            if ((stringValue is null) || !int.TryParse(stringValue, out int value))
+            {
+                // Hardware Intrinsic configuration knobs default to true
+                return true;
+            }
+
+            return value != 0;
+        }
+    }
+}
diff --git a/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx.csproj b/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx.csproj
new file mode 100644 (file)
index 0000000..eb0459f
--- /dev/null
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestTargetUnsupported Condition="('$(TargetArchitecture)' != 'x64' AND '$(TargetArchitecture)' != 'x86') OR ('$(RuntimeFlavor)' != 'coreclr')">true</CLRTestTargetUnsupported>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <AlwaysUseCrossGen2>true</AlwaysUseCrossGen2>
+    <IlasmRoundTripIncompatible>true</IlasmRoundTripIncompatible>
+    <NativeAotIncompatible>true</NativeAotIncompatible>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <CrossGen2TestExtraArguments>$(CrossGen2TestExtraArguments) --instruction-set:avx</CrossGen2TestExtraArguments>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="CpuId.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx2.csproj b/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx2.csproj
new file mode 100644 (file)
index 0000000..71375da
--- /dev/null
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestTargetUnsupported Condition="('$(TargetArchitecture)' != 'x64' AND '$(TargetArchitecture)' != 'x86') OR ('$(RuntimeFlavor)' != 'coreclr')">true</CLRTestTargetUnsupported>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <AlwaysUseCrossGen2>true</AlwaysUseCrossGen2>
+    <IlasmRoundTripIncompatible>true</IlasmRoundTripIncompatible>
+    <NativeAotIncompatible>true</NativeAotIncompatible>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <CrossGen2TestExtraArguments>$(CrossGen2TestExtraArguments) --instruction-set:avx2</CrossGen2TestExtraArguments>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="CpuId.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx512.csproj b/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx512.csproj
new file mode 100644 (file)
index 0000000..ebdb4f5
--- /dev/null
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestTargetUnsupported Condition="('$(TargetArchitecture)' != 'x64' AND '$(TargetArchitecture)' != 'x86') OR ('$(RuntimeFlavor)' != 'coreclr')">true</CLRTestTargetUnsupported>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <AlwaysUseCrossGen2>true</AlwaysUseCrossGen2>
+    <IlasmRoundTripIncompatible>true</IlasmRoundTripIncompatible>
+    <NativeAotIncompatible>true</NativeAotIncompatible>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <CrossGen2TestExtraArguments>$(CrossGen2TestExtraArguments) --instruction-set:avx512f,avx512f_vl,avx512bw,avx512bw_vl,avx512cd,avx512cd_vl,avx512dq,avx512dq_vl</CrossGen2TestExtraArguments>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="CpuId.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx_NoAvx2.csproj b/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Avx_NoAvx2.csproj
new file mode 100644 (file)
index 0000000..6bad67a
--- /dev/null
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestTargetUnsupported Condition="('$(TargetArchitecture)' != 'x64' AND '$(TargetArchitecture)' != 'x86') OR ('$(RuntimeFlavor)' != 'coreclr')">true</CLRTestTargetUnsupported>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <AlwaysUseCrossGen2>true</AlwaysUseCrossGen2>
+    <IlasmRoundTripIncompatible>true</IlasmRoundTripIncompatible>
+    <NativeAotIncompatible>true</NativeAotIncompatible>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <CrossGen2TestExtraArguments>$(CrossGen2TestExtraArguments) --instruction-set:avx,-avx2</CrossGen2TestExtraArguments>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="CpuId.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Baseline.csproj b/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Baseline.csproj
new file mode 100644 (file)
index 0000000..cfcfb94
--- /dev/null
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestTargetUnsupported Condition="('$(TargetArchitecture)' != 'x64' AND '$(TargetArchitecture)' != 'x86') OR ('$(RuntimeFlavor)' != 'coreclr')">true</CLRTestTargetUnsupported>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <AlwaysUseCrossGen2>true</AlwaysUseCrossGen2>
+    <IlasmRoundTripIncompatible>true</IlasmRoundTripIncompatible>
+    <NativeAotIncompatible>true</NativeAotIncompatible>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="CpuId.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Sse42.csproj b/src/tests/readytorun/HardwareIntrinsics/X86/CpuId_R2R_Sse42.csproj
new file mode 100644 (file)
index 0000000..39fa7fc
--- /dev/null
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestTargetUnsupported Condition="('$(TargetArchitecture)' != 'x64' AND '$(TargetArchitecture)' != 'x86') OR ('$(RuntimeFlavor)' != 'coreclr')">true</CLRTestTargetUnsupported>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <AlwaysUseCrossGen2>true</AlwaysUseCrossGen2>
+    <IlasmRoundTripIncompatible>true</IlasmRoundTripIncompatible>
+    <NativeAotIncompatible>true</NativeAotIncompatible>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <CrossGen2TestExtraArguments>$(CrossGen2TestExtraArguments) --instruction-set:sse4.2</CrossGen2TestExtraArguments>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="CpuId.cs" />
+  </ItemGroup>
+</Project>