From befd11d5fc597194dc398910d02f081bdc08865d Mon Sep 17 00:00:00 2001 From: Fei Peng Date: Mon, 12 Feb 2018 12:51:31 -0800 Subject: [PATCH] Implement SSE4.2 CompareGreaterThan --- .../X86/Shared/GenerateTests.csx | 18 ++ .../X86/Sse42/CompareGreaterThan.Int64.cs | 315 +++++++++++++++++++++ .../HardwareIntrinsics/X86/Sse42/Program.Sse42.cs | 19 ++ .../HardwareIntrinsics/X86/Sse42/Sse42_r.csproj | 38 +++ .../HardwareIntrinsics/X86/Sse42/Sse42_ro.csproj | 38 +++ 5 files changed, 428 insertions(+) create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse42/CompareGreaterThan.Int64.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse42/Program.Sse42.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_r.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_ro.csproj diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx index fe2639e..4a28eb3 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx @@ -166,6 +166,19 @@ private static readonly (string templateFileName, string[] templateData)[] Sse2I ("SimpleBinOpTest.template", new string[] { "Sse2", "Sse2", "Xor", "UInt64", "Vector128", "16", "(ulong)(random.Next(0, int.MaxValue))", "(ulong)(left[0] ^ right[0]) != result[0]", "(ulong)(left[i] ^ right[i]) != result[i]"}), }; +private static readonly (string templateFileName, string[] templateData)[] Sse41Inputs = new [] +{ + // TemplateName Isa, LoadIsa, Method, BaseType, VectorType, VectorSize, NextValue, ValidateFirstResult, ValidateRemainingResults + ("SimpleBinOpTest.template", new string[] { "Sse41", "Sse2", "CompareEqual", "Int64", "Vector128", "16", "(long)(random.Next(int.MinValue, int.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((long)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((long)(-1)) : 0)"}), + ("SimpleBinOpTest.template", new string[] { "Sse41", "Sse2", "CompareEqual", "UInt64", "Vector128", "16", "(ulong)(random.Next(0, int.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((ulong)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((ulong)(-1)) : 0)"}), +}; + +private static readonly (string templateFileName, string[] templateData)[] Sse42Inputs = new [] +{ + // TemplateName Isa, LoadIsa, Method, BaseType, VectorType, VectorSize, NextValue, ValidateFirstResult, ValidateRemainingResults + ("SimpleBinOpTest.template", new string[] { "Sse42", "Sse2", "CompareGreaterThan","Int64", "Vector128", "16", "(long)(random.Next(int.MinValue, int.MaxValue))", "result[0] != ((left[0] > right[0]) ? unchecked((long)(-1)) : 0)", "result[i] != ((left[i] > right[i]) ? unchecked((long)(-1)) : 0)"}), +}; + private static readonly (string templateFileName, string[] templateData)[] AvxInputs = new [] { // TemplateName Isa, LoadIsa, Method, BaseType, VectorType, VectorSize, NextValue, ValidateFirstResult, ValidateRemainingResults @@ -230,11 +243,14 @@ private static readonly (string templateFileName, string[] templateData)[] Avx2I ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "Byte", "Vector256", "32", "(byte)(random.Next(0, byte.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((byte)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((byte)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "Int16", "Vector256", "32", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((short)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((short)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "Int32", "Vector256", "32", "(int)(random.Next(int.MinValue, int.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((int)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((int)(-1)) : 0)"}), + ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "Int64", "Vector256", "32", "(long)(random.Next(int.MinValue, int.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((long)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((long)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "SByte", "Vector256", "32", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((sbyte)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((sbyte)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "UInt16", "Vector256", "32", "(ushort)(random.Next(0, ushort.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((ushort)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((ushort)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "UInt32", "Vector256", "32", "(uint)(random.Next(0, int.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((uint)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((uint)(-1)) : 0)"}), + ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareEqual", "UInt64", "Vector256", "32", "(ulong)(random.Next(0, int.MaxValue))", "result[0] != ((left[0] == right[0]) ? unchecked((ulong)(-1)) : 0)", "result[i] != ((left[i] == right[i]) ? unchecked((ulong)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareGreaterThan","Int16", "Vector256", "32", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[0] != ((left[0] > right[0]) ? unchecked((short)(-1)) : 0)", "result[i] != ((left[i] > right[i]) ? unchecked((short)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareGreaterThan","Int32", "Vector256", "32", "(int)(random.Next(int.MinValue, int.MaxValue))", "result[0] != ((left[0] > right[0]) ? unchecked((int)(-1)) : 0)", "result[i] != ((left[i] > right[i]) ? unchecked((int)(-1)) : 0)"}), + ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareGreaterThan","Int64", "Vector256", "32", "(long)(random.Next(int.MinValue, int.MaxValue))", "result[0] != ((left[0] > right[0]) ? unchecked((long)(-1)) : 0)", "result[i] != ((left[i] > right[i]) ? unchecked((long)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "CompareGreaterThan","SByte", "Vector256", "32", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))", "result[0] != ((left[0] > right[0]) ? unchecked((sbyte)(-1)) : 0)", "result[i] != ((left[i] > right[i]) ? unchecked((sbyte)(-1)) : 0)"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "Or", "Byte", "Vector256", "32", "(byte)(random.Next(0, byte.MaxValue))", "(byte)(left[0] | right[0]) != result[0]", "(byte)(left[i] | right[i]) != result[i]"}), ("SimpleBinOpTest.template", new string[] { "Avx2", "Avx", "Or", "Int16", "Vector256", "32", "(short)(random.Next(short.MinValue, short.MaxValue))", "(short)(left[0] | right[0]) != result[0]", "(short)(left[i] | right[i]) != result[i]"}), @@ -315,5 +331,7 @@ private static void ProcessInput(StreamWriter testListFile, (string templateFile ProcessInputs("Sse", SseInputs); ProcessInputs("Sse2", Sse2Inputs); +ProcessInputs("Sse41", Sse41Inputs); +ProcessInputs("Sse42", Sse42Inputs); ProcessInputs("Avx", AvxInputs); ProcessInputs("Avx2", Avx2Inputs); diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse42/CompareGreaterThan.Int64.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/CompareGreaterThan.Int64.cs new file mode 100644 index 0000000..e8bf19a --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/CompareGreaterThan.Int64.cs @@ -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 CompareGreaterThanInt64() + { + var test = new SimpleBinaryOpTest__CompareGreaterThanInt64(); + + 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__CompareGreaterThanInt64 + { + 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 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__CompareGreaterThanInt64() + { + 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, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__CompareGreaterThanInt64() + { + 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, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(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(_data1, _data2, new Int64[ElementCount], VectorSize); + } + + public bool IsSupported => Sse42.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Sse42.CompareGreaterThan( + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Sse42.CompareGreaterThan( + 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 = Sse42.CompareGreaterThan( + 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(Sse42).GetMethod(nameof(Sse42.CompareGreaterThan), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Sse42).GetMethod(nameof(Sse42.CompareGreaterThan), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int64*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int64*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Sse42).GetMethod(nameof(Sse42.CompareGreaterThan), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int64*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Sse42.CompareGreaterThan( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var left = Unsafe.Read>(_dataTable.inArray1Ptr); + var right = Unsafe.Read>(_dataTable.inArray2Ptr); + var result = Sse42.CompareGreaterThan(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 = Sse42.CompareGreaterThan(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 = Sse42.CompareGreaterThan(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__CompareGreaterThanInt64(); + var result = Sse42.CompareGreaterThan(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Sse42.CompareGreaterThan(_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 left, Vector128 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(ref outArray[0]), ref Unsafe.AsRef(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(ref inArray1[0]), ref Unsafe.AsRef(left), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray2[0]), ref Unsafe.AsRef(right), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(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(Sse42)}.{nameof(Sse42.CompareGreaterThan)}: {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/Sse42/Program.Sse42.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Program.Sse42.cs new file mode 100644 index 0000000..50e2755 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Program.Sse42.cs @@ -0,0 +1,19 @@ +// 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() { + ["CompareGreaterThan.Int64"] = CompareGreaterThanInt64, + }; + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_r.csproj new file mode 100644 index 0000000..71a28e0 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_r.csproj @@ -0,0 +1,38 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + None + + + + + + + + + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_ro.csproj new file mode 100644 index 0000000..cf4ebc8 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse42/Sse42_ro.csproj @@ -0,0 +1,38 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + None + True + + + + + + + + + + + + + + -- 2.7.4