Implement SSE4.1 CompareEqual
authorFei Peng <fei.peng@intel.com>
Mon, 12 Feb 2018 20:50:51 +0000 (12:50 -0800)
committerTanner Gooding <tagoo@outlook.com>
Tue, 13 Feb 2018 02:54:18 +0000 (18:54 -0800)
src/jit/hwintrinsiclistxarch.h
tests/src/JIT/HardwareIntrinsics/X86/Sse41/CompareEqual.Int64.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse41/CompareEqual.UInt64.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse41/Program.Sse41.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse41/Sse41_r.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse41/Sse41_ro.csproj [new file with mode: 0644]

index 16ebba3..1b68ea8 100644 (file)
@@ -189,12 +189,14 @@ HARDWARE_INTRINSIC(SSSE3_IsSupported,                                "get_IsSupp
 
 //  SSE41 Intrinsics
 HARDWARE_INTRINSIC(SSE41_IsSupported,                                "get_IsSupported",                                  SSE41,      -1,           0,            0,           {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid},           HW_Category_IsSupportedProperty,               HW_Flag_NoFlag)
-HARDWARE_INTRINSIC(SSE41_Multiply,                                   "Multiply",                                         SSE41,      -1,           16,           2,           {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_pmuldq,    INS_invalid,   INS_invalid,   INS_invalid},           HW_Category_SimpleSIMD,                        HW_Flag_Commutative)
 HARDWARE_INTRINSIC(SSE41_BlendVariable,                              "BlendVariable",                                    SSE41,      -1,           16,           3,           {INS_pblendvb,  INS_pblendvb,  INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_blendvps,  INS_blendvpd},          HW_Category_SimpleSIMD,                        HW_Flag_NoFlag)
+HARDWARE_INTRINSIC(SSE41_CompareEqual,                               "CompareEqual",                                     SSE41,      -1,           16,           2,           {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_pcmpeqq,   INS_pcmpeqq,   INS_invalid,   INS_invalid},           HW_Category_SimpleSIMD,                        HW_Flag_Commutative)
+HARDWARE_INTRINSIC(SSE41_Multiply,                                   "Multiply",                                         SSE41,      -1,           16,           2,           {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_pmuldq,    INS_invalid,   INS_invalid,   INS_invalid},           HW_Category_SimpleSIMD,                        HW_Flag_Commutative)
 
 //  SSE42 Intrinsics
 HARDWARE_INTRINSIC(SSE42_IsSupported,                                "get_IsSupported",                                  SSE42,      -1,           0,            0,           {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid},           HW_Category_IsSupportedProperty,               HW_Flag_NoFlag)
 HARDWARE_INTRINSIC(SSE42_Crc32,                                      "Crc32",                                            SSE42,      -1,           0,            2,           {INS_invalid,   INS_crc32,     INS_invalid,   INS_crc32,     INS_invalid,   INS_crc32,     INS_invalid,   INS_crc32,     INS_invalid,   INS_invalid},           HW_Category_Scalar,                            HW_Flag_NoFloatingPointUsed)
+HARDWARE_INTRINSIC(SSE42_CompareGreaterThan,                         "CompareGreaterThan",                               SSE42,      -1,           16,           2,           {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_pcmpgtq,   INS_invalid,   INS_invalid,   INS_invalid},           HW_Category_SimpleSIMD,                        HW_Flag_NoFlag)
 
 // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
 //                  Intrinsic ID                                      Function name                                      ISA        ival       SIMD size       NumArg                                                                   instructions                                                                                              Category                                         Flags
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse41/CompareEqual.Int64.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse41/CompareEqual.Int64.cs
new file mode 100644 (file)
index 0000000..3759d72
--- /dev/null
@@ -0,0 +1,315 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void CompareEqualInt64()
+        {
+            var test = new SimpleBinaryOpTest__CompareEqualInt64();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                // Validates basic functionality works, using Load
+                test.RunBasicScenario_Load();
+
+                // Validates basic functionality works, using LoadAligned
+                test.RunBasicScenario_LoadAligned();
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                // Validates calling via reflection works, using Load
+                test.RunReflectionScenario_Load();
+
+                // Validates calling via reflection works, using LoadAligned
+                test.RunReflectionScenario_LoadAligned();
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                // Validates passing a local works, using Load
+                test.RunLclVarScenario_Load();
+
+                // Validates passing a local works, using LoadAligned
+                test.RunLclVarScenario_LoadAligned();
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__CompareEqualInt64
+    {
+        private const int VectorSize = 16;
+        private const int ElementCount = VectorSize / sizeof(Int64);
+
+        private static Int64[] _data1 = new Int64[ElementCount];
+        private static Int64[] _data2 = new Int64[ElementCount];
+
+        private static Vector128<Int64> _clsVar1;
+        private static Vector128<Int64> _clsVar2;
+
+        private Vector128<Int64> _fld1;
+        private Vector128<Int64> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<Int64> _dataTable;
+
+        static SimpleBinaryOpTest__CompareEqualInt64()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < ElementCount; i++) { _data1[i] = (long)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (long)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int64>, byte>(ref _clsVar1), ref Unsafe.As<Int64, byte>(ref _data2[0]), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int64>, byte>(ref _clsVar2), ref Unsafe.As<Int64, byte>(ref _data1[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__CompareEqualInt64()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < ElementCount; i++) { _data1[i] = (long)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (long)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int64>, byte>(ref _fld1), ref Unsafe.As<Int64, byte>(ref _data1[0]), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int64>, byte>(ref _fld2), ref Unsafe.As<Int64, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < ElementCount; i++) { _data1[i] = (long)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (long)(random.Next(int.MinValue, int.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<Int64>(_data1, _data2, new Int64[ElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse41.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse41.CompareEqual(
+                Unsafe.Read<Vector128<Int64>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int64>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse41.CompareEqual(
+                Sse2.LoadVector128((Int64*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int64*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse41.CompareEqual(
+                Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse41).GetMethod(nameof(Sse41.CompareEqual), new Type[] { typeof(Vector128<Int64>), typeof(Vector128<Int64>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int64>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int64>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int64>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse41).GetMethod(nameof(Sse41.CompareEqual), new Type[] { typeof(Vector128<Int64>), typeof(Vector128<Int64>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int64*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int64*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int64>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse41).GetMethod(nameof(Sse41.CompareEqual), new Type[] { typeof(Vector128<Int64>), typeof(Vector128<Int64>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int64>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse41.CompareEqual(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int64>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int64>>(_dataTable.inArray2Ptr);
+            var result = Sse41.CompareEqual(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int64*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int64*)(_dataTable.inArray2Ptr));
+            var result = Sse41.CompareEqual(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray2Ptr));
+            var result = Sse41.CompareEqual(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__CompareEqualInt64();
+            var result = Sse41.CompareEqual(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse41.CompareEqual(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int64> left, Vector128<Int64> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int64[] inArray1 = new Int64[ElementCount];
+            Int64[] inArray2 = new Int64[ElementCount];
+            Int64[] outArray = new Int64[ElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int64, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(void* left, void* right, void* result, [CallerMemberName] string method = "")
+        {
+            Int64[] inArray1 = new Int64[ElementCount];
+            Int64[] inArray2 = new Int64[ElementCount];
+            Int64[] outArray = new Int64[ElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int64, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int64, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int64, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int64[] left, Int64[] right, Int64[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != ((left[0] == right[0]) ? unchecked((long)(-1)) : 0))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < left.Length; i++)
+                {
+                    if (result[i] != ((left[i] == right[i]) ? unchecked((long)(-1)) : 0))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse41)}.{nameof(Sse41.CompareEqual)}<Int64>: {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse41/CompareEqual.UInt64.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse41/CompareEqual.UInt64.cs
new file mode 100644 (file)
index 0000000..146847f
--- /dev/null
@@ -0,0 +1,315 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void CompareEqualUInt64()
+        {
+            var test = new SimpleBinaryOpTest__CompareEqualUInt64();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                // Validates basic functionality works, using Load
+                test.RunBasicScenario_Load();
+
+                // Validates basic functionality works, using LoadAligned
+                test.RunBasicScenario_LoadAligned();
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                // Validates calling via reflection works, using Load
+                test.RunReflectionScenario_Load();
+
+                // Validates calling via reflection works, using LoadAligned
+                test.RunReflectionScenario_LoadAligned();
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                // Validates passing a local works, using Load
+                test.RunLclVarScenario_Load();
+
+                // Validates passing a local works, using LoadAligned
+                test.RunLclVarScenario_LoadAligned();
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__CompareEqualUInt64
+    {
+        private const int VectorSize = 16;
+        private const int ElementCount = VectorSize / sizeof(UInt64);
+
+        private static UInt64[] _data1 = new UInt64[ElementCount];
+        private static UInt64[] _data2 = new UInt64[ElementCount];
+
+        private static Vector128<UInt64> _clsVar1;
+        private static Vector128<UInt64> _clsVar2;
+
+        private Vector128<UInt64> _fld1;
+        private Vector128<UInt64> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<UInt64> _dataTable;
+
+        static SimpleBinaryOpTest__CompareEqualUInt64()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < ElementCount; i++) { _data1[i] = (ulong)(random.Next(0, int.MaxValue)); _data2[i] = (ulong)(random.Next(0, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<UInt64>, byte>(ref _clsVar1), ref Unsafe.As<UInt64, byte>(ref _data2[0]), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<UInt64>, byte>(ref _clsVar2), ref Unsafe.As<UInt64, byte>(ref _data1[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__CompareEqualUInt64()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < ElementCount; i++) { _data1[i] = (ulong)(random.Next(0, int.MaxValue)); _data2[i] = (ulong)(random.Next(0, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<UInt64>, byte>(ref _fld1), ref Unsafe.As<UInt64, byte>(ref _data1[0]), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<UInt64>, byte>(ref _fld2), ref Unsafe.As<UInt64, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < ElementCount; i++) { _data1[i] = (ulong)(random.Next(0, int.MaxValue)); _data2[i] = (ulong)(random.Next(0, int.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<UInt64>(_data1, _data2, new UInt64[ElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse41.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse41.CompareEqual(
+                Unsafe.Read<Vector128<UInt64>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<UInt64>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse41.CompareEqual(
+                Sse2.LoadVector128((UInt64*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((UInt64*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse41.CompareEqual(
+                Sse2.LoadAlignedVector128((UInt64*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((UInt64*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse41).GetMethod(nameof(Sse41.CompareEqual), new Type[] { typeof(Vector128<UInt64>), typeof(Vector128<UInt64>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<UInt64>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<UInt64>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt64>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse41).GetMethod(nameof(Sse41.CompareEqual), new Type[] { typeof(Vector128<UInt64>), typeof(Vector128<UInt64>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((UInt64*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((UInt64*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt64>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse41).GetMethod(nameof(Sse41.CompareEqual), new Type[] { typeof(Vector128<UInt64>), typeof(Vector128<UInt64>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((UInt64*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((UInt64*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt64>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse41.CompareEqual(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<UInt64>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<UInt64>>(_dataTable.inArray2Ptr);
+            var result = Sse41.CompareEqual(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((UInt64*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((UInt64*)(_dataTable.inArray2Ptr));
+            var result = Sse41.CompareEqual(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((UInt64*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((UInt64*)(_dataTable.inArray2Ptr));
+            var result = Sse41.CompareEqual(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__CompareEqualUInt64();
+            var result = Sse41.CompareEqual(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse41.CompareEqual(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<UInt64> left, Vector128<UInt64> right, void* result, [CallerMemberName] string method = "")
+        {
+            UInt64[] inArray1 = new UInt64[ElementCount];
+            UInt64[] inArray2 = new UInt64[ElementCount];
+            UInt64[] outArray = new UInt64[ElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt64, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(void* left, void* right, void* result, [CallerMemberName] string method = "")
+        {
+            UInt64[] inArray1 = new UInt64[ElementCount];
+            UInt64[] inArray2 = new UInt64[ElementCount];
+            UInt64[] outArray = new UInt64[ElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt64, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt64, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt64, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(UInt64[] left, UInt64[] right, UInt64[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != ((left[0] == right[0]) ? unchecked((ulong)(-1)) : 0))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < left.Length; i++)
+                {
+                    if (result[i] != ((left[i] == right[i]) ? unchecked((ulong)(-1)) : 0))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse41)}.{nameof(Sse41.CompareEqual)}<UInt64>: {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse41/Program.Sse41.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse41/Program.Sse41.cs
new file mode 100644 (file)
index 0000000..a71a4b1
--- /dev/null
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        static Program()
+        {
+            TestList = new Dictionary<string, Action>() {
+                ["CompareEqual.Int64"] = CompareEqualInt64,
+                ["CompareEqual.UInt64"] = CompareEqualUInt64,
+            };
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse41/Sse41_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse41/Sse41_r.csproj
new file mode 100644 (file)
index 0000000..3ca03a2
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize></Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="CompareEqual.Int64.cs" />
+    <Compile Include="CompareEqual.UInt64.cs" />
+    <Compile Include="Program.Sse41.cs" />
+    <Compile Include="..\Shared\Program.cs" />
+    <Compile Include="..\Shared\SimpleBinOpTest_DataTable.cs" />    
+    <Compile Include="..\Shared\SimpleTernOpTest_DataTable.cs" />  
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse41/Sse41_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse41/Sse41_ro.csproj
new file mode 100644 (file)
index 0000000..93a8455
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="CompareEqual.Int64.cs" />
+    <Compile Include="CompareEqual.UInt64.cs" />
+    <Compile Include="Program.Sse41.cs" />
+    <Compile Include="..\Shared\Program.cs" />
+    <Compile Include="..\Shared\SimpleBinOpTest_DataTable.cs" />    
+    <Compile Include="..\Shared\SimpleTernOpTest_DataTable.cs" />  
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>