From 77af5c8d60b8088c00945b2512c97a0d3679d400 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 16 Feb 2018 12:13:05 -0800 Subject: [PATCH] Adding tests for the SSE3 and SSSE3 hardware intrinsics --- .../X86/Shared/AlternatingBinOpTest.template | 329 ++++++++++++++++++++ .../X86/Shared/AlternatingBinOpTest_DataTable.cs | 65 ++++ .../X86/Shared/GenerateTests.csx | 33 ++ .../X86/Shared/HorizontalBinOpTest.template | 336 +++++++++++++++++++++ .../X86/Shared/HorizontalBinOpTest_DataTable.cs | 65 ++++ .../X86/Sse3/AddSubtract.Double.cs | 329 ++++++++++++++++++++ .../X86/Sse3/AddSubtract.Single.cs | 329 ++++++++++++++++++++ .../X86/Sse3/HorizontalAdd.Double.cs | 336 +++++++++++++++++++++ .../X86/Sse3/HorizontalAdd.Single.cs | 336 +++++++++++++++++++++ .../X86/Sse3/HorizontalSubtract.Double.cs | 336 +++++++++++++++++++++ .../X86/Sse3/HorizontalSubtract.Single.cs | 336 +++++++++++++++++++++ .../X86/Sse3/MoveAndDuplicate.cs | 86 ++++++ .../X86/Sse3/MoveAndDuplicate_r.csproj | 34 +++ .../X86/Sse3/MoveAndDuplicate_ro.csproj | 34 +++ .../X86/Sse3/MoveHighAndDuplicate.cs | 88 ++++++ .../X86/Sse3/MoveHighAndDuplicate_r.csproj | 34 +++ .../X86/Sse3/MoveHighAndDuplicate_ro.csproj | 34 +++ .../X86/Sse3/MoveLowAndDuplicate.cs | 88 ++++++ .../X86/Sse3/MoveLowAndDuplicate_r.csproj | 34 +++ .../X86/Sse3/MoveLowAndDuplicate_ro.csproj | 34 +++ .../HardwareIntrinsics/X86/Sse3/Program.Sse3.cs | 24 ++ .../JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj | 43 +++ .../JIT/HardwareIntrinsics/X86/Sse3/Sse3_ro.csproj | 43 +++ .../JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs | 306 +++++++++++++++++++ .../JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs | 306 +++++++++++++++++++ .../JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs | 306 +++++++++++++++++++ .../JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs | 156 ++++++++++ .../X86/Ssse3/AlignRight_r.csproj | 34 +++ .../X86/Ssse3/AlignRight_ro.csproj | 34 +++ .../X86/Ssse3/HorizontalAdd.Int16.cs | 336 +++++++++++++++++++++ .../X86/Ssse3/HorizontalAdd.Int32.cs | 336 +++++++++++++++++++++ .../X86/Ssse3/HorizontalAddSaturate.Int16.cs | 336 +++++++++++++++++++++ .../X86/Ssse3/HorizontalSubtract.Int16.cs | 336 +++++++++++++++++++++ .../X86/Ssse3/HorizontalSubtract.Int32.cs | 336 +++++++++++++++++++++ .../X86/Ssse3/HorizontalSubtractSaturate.Int16.cs | 336 +++++++++++++++++++++ .../X86/Ssse3/MultiplyAddAdjacent.Int16.cs | 330 ++++++++++++++++++++ .../X86/Ssse3/MultiplyHighRoundScale.Int16.cs | 330 ++++++++++++++++++++ .../HardwareIntrinsics/X86/Ssse3/Program.Ssse3.cs | 33 ++ .../HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs | 330 ++++++++++++++++++++ .../JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int16.cs | 330 ++++++++++++++++++++ .../JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int32.cs | 330 ++++++++++++++++++++ .../JIT/HardwareIntrinsics/X86/Ssse3/Sign.SByte.cs | 330 ++++++++++++++++++++ .../HardwareIntrinsics/X86/Ssse3/Ssse3_r.csproj | 53 ++++ .../HardwareIntrinsics/X86/Ssse3/Ssse3_ro.csproj | 53 ++++ 44 files changed, 8683 insertions(+) create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest.template create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest_DataTable.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest.template create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest_DataTable.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Double.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Single.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Double.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Single.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Double.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Single.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_r.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_ro.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_r.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_ro.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_r.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_ro.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/Program.Sse3.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_ro.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_r.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_ro.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int32.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAddSaturate.Int16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int32.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtractSaturate.Int16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyAddAdjacent.Int16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyHighRoundScale.Int16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Program.Ssse3.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int16.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int32.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.SByte.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_r.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_ro.csproj diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest.template b/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest.template new file mode 100644 index 0000000..f455f4b --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest.template @@ -0,0 +1,329 @@ +// 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 {2}{4}() + {{ + var test = new AlternatingBinaryOpTest__{2}{4}(); + + if (test.IsSupported) + {{ + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({1}.IsSupported) + {{ + // 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(); + + if ({1}.IsSupported) + {{ + // 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(); + + if ({1}.IsSupported) + {{ + // 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 AlternatingBinaryOpTest__{2}{4} + {{ + private const int VectorSize = {9}; + + private const int Op1ElementCount = VectorSize / sizeof({6}); + private const int Op2ElementCount = VectorSize / sizeof({8}); + private const int RetElementCount = VectorSize / sizeof({4}); + + private static {6}[] _data1 = new {6}[Op1ElementCount]; + private static {8}[] _data2 = new {8}[Op2ElementCount]; + + private static {5}<{6}> _clsVar1; + private static {7}<{8}> _clsVar2; + + private {5}<{6}> _fld1; + private {7}<{8}> _fld2; + + private AlternatingBinaryOpTest__DataTable<{4}, {6}, {8}> _dataTable; + + static AlternatingBinaryOpTest__{2}{4}() + {{ + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _clsVar1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _clsVar2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize); + }} + + public AlternatingBinaryOpTest__{2}{4}() + {{ + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _fld1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _fld2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }} + for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }} + _dataTable = new AlternatingBinaryOpTest__DataTable<{4}, {6}, {8}>(_data1, _data2, new {4}[RetElementCount], VectorSize); + }} + + public bool IsSupported => {0}.IsSupported; + + public bool Succeeded {{ get; set; }} + + public void RunBasicScenario_UnsafeRead() + {{ + var result = {0}.{2}( + Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunBasicScenario_Load() + {{ + var result = {0}.{2}( + {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunBasicScenario_LoadAligned() + {{ + var result = {0}.{2}( + {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunReflectionScenario_UnsafeRead() + {{ + var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }}) + .Invoke(null, new object[] {{ + Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr) + }}); + + Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunReflectionScenario_Load() + {{ + var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }}) + .Invoke(null, new object[] {{ + {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr)) + }}); + + Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunReflectionScenario_LoadAligned() + {{ + var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }}) + .Invoke(null, new object[] {{ + {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr)) + }}); + + Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunClsVarScenario() + {{ + var result = {0}.{2}( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr); + }} + + public void RunLclVarScenario_UnsafeRead() + {{ + var left = Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr); + var right = Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr); + var result = {0}.{2}(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + }} + + public void RunLclVarScenario_Load() + {{ + var left = {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)); + var right = {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr)); + var result = {0}.{2}(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + }} + + public void RunLclVarScenario_LoadAligned() + {{ + var left = {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)); + var right = {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr)); + var result = {0}.{2}(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + }} + + public void RunLclFldScenario() + {{ + var test = new AlternatingBinaryOpTest__{2}{4}(); + var result = {0}.{2}(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + }} + + public void RunFldScenario() + {{ + var result = {0}.{2}(_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({5}<{6}> left, {7}<{8}> right, void* result, [CallerMemberName] string method = "") + {{ + {6}[] inArray1 = new {6}[Op1ElementCount]; + {8}[] inArray2 = new {8}[Op2ElementCount]; + {4}[] outArray = new {4}[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(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 = "") + {{ + {6}[] inArray1 = new {6}[Op1ElementCount]; + {8}[] inArray2 = new {8}[Op2ElementCount]; + {4}[] outArray = new {4}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{6}, byte>(ref inArray1[0]), ref Unsafe.AsRef(left), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{8}, byte>(ref inArray2[0]), ref Unsafe.AsRef(right), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + }} + + private void ValidateResult({6}[] left, {8}[] right, {4}[] result, [CallerMemberName] string method = "") + {{ + for (var i = 0; i < RetElementCount; i += 2) + {{ + if ({12}) + {{ + Succeeded = false; + break; + }} + + if ({13}) + {{ + Succeeded = false; + break; + }} + }} + + if (!Succeeded) + {{ + Console.WriteLine($"{{nameof({0})}}.{{nameof({0}.{2})}}<{4}>({5}<{6}>, {7}<{8}>): {{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/Shared/AlternatingBinOpTest_DataTable.cs b/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest_DataTable.cs new file mode 100644 index 0000000..a2f9977 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest_DataTable.cs @@ -0,0 +1,65 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public unsafe struct AlternatingBinaryOpTest__DataTable : IDisposable + where TResult : struct + where TOp1 : struct + where TOp2 : struct + { + private byte[] inArray1; + private byte[] inArray2; + private byte[] outArray; + + private GCHandle inHandle1; + private GCHandle inHandle2; + private GCHandle outHandle; + + private byte simdSize; + + public AlternatingBinaryOpTest__DataTable(TOp1[] inArray1, TOp2[] inArray2, TResult[] outArray, int simdSize) + { + this.inArray1 = new byte[simdSize * 2]; + this.inArray2 = new byte[simdSize * 2]; + this.outArray = new byte[simdSize * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); + + this.simdSize = unchecked((byte)(simdSize)); + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As(ref inArray1[0]), this.simdSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray2Ptr), ref Unsafe.As(ref inArray2[0]), this.simdSize); + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), simdSize); + public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), simdSize); + public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), simdSize); + + public void Dispose() + { + inHandle1.Free(); + inHandle2.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, byte expectedAlignment) + { + // Compute how bad the misalignment is, which is at most (expectedAlignment - 1). + // Then subtract that from the expectedAlignment and add it to the original address + // to compute the aligned address. + + var misalignment = expectedAlignment - ((ulong)(buffer) % expectedAlignment); + return (void*)(buffer + misalignment); + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx index 2827f95..4351de6 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx @@ -166,6 +166,37 @@ private static readonly (string templateFileName, string[] templateData)[] Sse2I ("SimpleBinOpTest.template", new string[] { "Sse2", "Sse2", "Xor", "Vector128", "UInt64", "Vector128", "UInt64", "Vector128", "UInt64", "16", "(ulong)(random.Next(0, int.MaxValue))", "(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)[] Sse3Inputs = new [] +{ + // TemplateName Isa, LoadIsa, Method, RetVectorType, RetBaseType, Op1VectorType, Op1BaseType, Op2VectorType, Op2BaseType, Op3VectorType, Op3BaseType, VectorSize, NextValueOp1, NextValueOp2, NextValueOp3, ValidateFirstResult, ValidateRemainingResults + ("AlternatingBinOpTest.template", new string[] { "Sse3", "Sse2", "AddSubtract", "Vector128", "Double", "Vector128", "Double", "Vector128", "Double", "16", "(double)(random.NextDouble())", "(double)(random.NextDouble())", "BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits(left[i] - right[i])", "BitConverter.DoubleToInt64Bits(result[i + 1]) != BitConverter.DoubleToInt64Bits(left[i + 1] + right[i + 1])"}), + ("AlternatingBinOpTest.template", new string[] { "Sse3", "Sse" , "AddSubtract", "Vector128", "Single", "Vector128", "Single", "Vector128", "Single", "16", "(float)(random.NextDouble())", "(float)(random.NextDouble())", "BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(left[i] - right[i])", "BitConverter.SingleToInt32Bits(result[i + 1]) != BitConverter.SingleToInt32Bits(left[i + 1] + right[i + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Sse3", "Sse2", "HorizontalAdd", "Vector128", "Double", "Vector128", "Double", "Vector128", "Double", "16", "(double)(random.NextDouble())", "(double)(random.NextDouble())", "BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] + left[i3 + 1])", "BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] + right[i3 + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Sse3", "Sse", "HorizontalAdd", "Vector128", "Single", "Vector128", "Single", "Vector128", "Single", "16", "(float)(random.NextDouble())", "(float)(random.NextDouble())", "BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] + left[i3 + 1])", "BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] + right[i3 + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Sse3", "Sse2", "HorizontalSubtract", "Vector128", "Double", "Vector128", "Double", "Vector128", "Double", "16", "(double)(random.NextDouble())", "(double)(random.NextDouble())", "BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] - left[i3 + 1])", "BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] - right[i3 + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Sse3", "Sse", "HorizontalSubtract", "Vector128", "Single", "Vector128", "Single", "Vector128", "Single", "16", "(float)(random.NextDouble())", "(float)(random.NextDouble())", "BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] - left[i3 + 1])", "BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] - right[i3 + 1])"}), +}; + +private static readonly (string templateFileName, string[] templateData)[] Ssse3Inputs = new [] +{ + // TemplateName Isa, LoadIsa, Method, RetVectorType, RetBaseType, Op1VectorType, Op1BaseType, Op2VectorType, Op2BaseType, Op3VectorType, Op3BaseType, VectorSize, NextValueOp1, NextValueOp2, NextValueOp3, ValidateFirstResult, ValidateRemainingResults + ("SimpleUnOpTest.template", new string[] { "Ssse3", "Sse2", "Abs", "Vector128", "Byte", "Vector128", "SByte", "16", "(sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue))", "result[0] != Math.Abs(firstOp[0])", "result[i] != Math.Abs(firstOp[i])"}), + ("SimpleUnOpTest.template", new string[] { "Ssse3", "Sse2", "Abs", "Vector128", "UInt16", "Vector128", "Int16", "16", "(short)(random.Next(short.MinValue + 1, short.MaxValue))", "result[0] != Math.Abs(firstOp[0])", "result[i] != Math.Abs(firstOp[i])"}), + ("SimpleUnOpTest.template", new string[] { "Ssse3", "Sse2", "Abs", "Vector128", "UInt32", "Vector128", "Int32", "16", "(int)(random.Next(int.MinValue + 1, int.MaxValue))", "result[0] != Math.Abs(firstOp[0])", "result[i] != Math.Abs(firstOp[i])"}), + ("HorizontalBinOpTest.template", new string[] { "Ssse3", "Sse2", "HorizontalAdd", "Vector128", "Int16", "Vector128", "Int16", "Vector128", "Int16", "16", "(short)(random.Next(short.MinValue, short.MaxValue))", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[i1] != (short)(left[i3] + left[i3 + 1])", "result[i2] != (short)(right[i3] + right[i3 + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Ssse3", "Sse2", "HorizontalAdd", "Vector128", "Int32", "Vector128", "Int32", "Vector128", "Int32", "16", "(int)(random.Next(int.MinValue, int.MaxValue))", "(int)(random.Next(int.MinValue, int.MaxValue))", "result[i1] != (int)(left[i3] + left[i3 + 1])", "result[i2] != (int)(right[i3] + right[i3 + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Ssse3", "Sse2", "HorizontalAddSaturate", "Vector128", "Int16", "Vector128", "Int16", "Vector128", "Int16", "16", "(short)(random.Next(short.MinValue, short.MaxValue))", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[i1] != Math.Clamp((left[i3] + left[i3 + 1]), short.MinValue, short.MaxValue)", "result[i2] != Math.Clamp((right[i3] + right[i3 + 1]), short.MinValue, short.MaxValue)"}), + ("HorizontalBinOpTest.template", new string[] { "Ssse3", "Sse2", "HorizontalSubtract", "Vector128", "Int16", "Vector128", "Int16", "Vector128", "Int16", "16", "(short)(random.Next(short.MinValue, short.MaxValue))", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[i1] != (short)(left[i3] - left[i3 + 1])", "result[i2] != (short)(right[i3] - right[i3 + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Ssse3", "Sse2", "HorizontalSubtract", "Vector128", "Int32", "Vector128", "Int32", "Vector128", "Int32", "16", "(int)(random.Next(int.MinValue, int.MaxValue))", "(int)(random.Next(int.MinValue, int.MaxValue))", "result[i1] != (int)(left[i3] - left[i3 + 1])", "result[i2] != (int)(right[i3] - right[i3 + 1])"}), + ("HorizontalBinOpTest.template", new string[] { "Ssse3", "Sse2", "HorizontalSubtractSaturate", "Vector128", "Int16", "Vector128", "Int16", "Vector128", "Int16", "16", "(short)(random.Next(short.MinValue, short.MaxValue))", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[i1] != Math.Clamp((left[i3] - left[i3 + 1]), short.MinValue, short.MaxValue)", "result[i2] != Math.Clamp((right[i3] - right[i3 + 1]), short.MinValue, short.MaxValue)"}), + ("SimpleBinOpTest.template", new string[] { "Ssse3", "Sse2", "MultiplyAddAdjacent", "Vector128", "Int16", "Vector128", "Byte", "Vector128", "SByte", "16", "(byte)(random.Next(0, byte.MaxValue))", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))", "result[0] != Math.Clamp(((right[1] * left[1]) + (right[0] * left[0])), short.MinValue, short.MaxValue)", "result[i] != Math.Clamp(((right[(i * 2) + 1] * left[(i * 2) + 1]) + (right[i * 2] * left[i * 2])), short.MinValue, short.MaxValue)"}), + ("SimpleBinOpTest.template", new string[] { "Ssse3", "Sse2", "MultiplyHighRoundScale", "Vector128", "Int16", "Vector128", "Int16", "Vector128", "Int16", "16", "(short)(random.Next(short.MinValue, short.MaxValue))", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[0] != (short)((((left[0] * right[0]) >> 14) + 1) >> 1)", "result[i] != (short)((((left[i] * right[i]) >> 14) + 1) >> 1)"}), + ("SimpleBinOpTest.template", new string[] { "Ssse3", "Sse2", "Shuffle", "Vector128", "SByte", "Vector128", "SByte", "Vector128", "SByte", "16", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))", "result[0] != ((right[0] < 0) ? 0 : left[right[0] & 0x0F])", "result[i] != ((right[i] < 0) ? 0 : left[right[i] & 0x0F])"}), + ("SimpleBinOpTest.template", new string[] { "Ssse3", "Sse2", "Sign", "Vector128", "SByte", "Vector128", "SByte", "Vector128", "SByte", "16", "(sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue))", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))", "result[0] != (right[0] < 0 ? (sbyte)(-left[0]) : (right[0] > 0 ? left[0] : 0))", "result[i] != (right[i] < 0 ? (sbyte)(-left[i]) : (right[i] > 0 ? left[i] : 0))"}), + ("SimpleBinOpTest.template", new string[] { "Ssse3", "Sse2", "Sign", "Vector128", "Int16", "Vector128", "Int16", "Vector128", "Int16", "16", "(short)(random.Next(short.MinValue + 1, short.MaxValue))", "(short)(random.Next(short.MinValue, short.MaxValue))", "result[0] != (right[0] < 0 ? (short)(-left[0]) : (right[0] > 0 ? left[0] : 0))", "result[i] != (right[i] < 0 ? (short)(-left[i]) : (right[i] > 0 ? left[i] : 0))"}), + ("SimpleBinOpTest.template", new string[] { "Ssse3", "Sse2", "Sign", "Vector128", "Int32", "Vector128", "Int32", "Vector128", "Int32", "16", "(int)(random.Next(int.MinValue + 1, int.MaxValue))", "(int)(random.Next(int.MinValue, int.MaxValue))", "result[0] != (right[0] < 0 ? (int)(-left[0]) : (right[0] > 0 ? left[0] : 0))", "result[i] != (right[i] < 0 ? (int)(-left[i]) : (right[i] > 0 ? left[i] : 0))"}), +}; + private static readonly (string templateFileName, string[] templateData)[] Sse41Inputs = new [] { // TemplateName Isa, LoadIsa, Method, RetVectorType, RetBaseType, Op1VectorType, Op1BaseType, Op2VectorType, Op2BaseType, Op3VectorType, Op3BaseType, VectorSize, NextValueOp1, NextValueOp2, NextValueOp3, ValidateFirstResult, ValidateRemainingResults @@ -333,6 +364,8 @@ private static void ProcessInput(StreamWriter testListFile, (string templateFile ProcessInputs("Sse", SseInputs); ProcessInputs("Sse2", Sse2Inputs); +ProcessInputs("Sse3", Sse3Inputs); +ProcessInputs("Ssse3", Ssse3Inputs); ProcessInputs("Sse41", Sse41Inputs); ProcessInputs("Sse42", Sse42Inputs); ProcessInputs("Avx", AvxInputs); diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest.template b/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest.template new file mode 100644 index 0000000..c3a45df --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest.template @@ -0,0 +1,336 @@ +// 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 {2}{4}() + {{ + var test = new HorizontalBinaryOpTest__{2}{4}(); + + if (test.IsSupported) + {{ + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({1}.IsSupported) + {{ + // 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(); + + if ({1}.IsSupported) + {{ + // 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(); + + if ({1}.IsSupported) + {{ + // 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 HorizontalBinaryOpTest__{2}{4} + {{ + private const int VectorSize = {9}; + + private const int Op1ElementCount = VectorSize / sizeof({6}); + private const int Op2ElementCount = VectorSize / sizeof({8}); + private const int RetElementCount = VectorSize / sizeof({4}); + + private static {6}[] _data1 = new {6}[Op1ElementCount]; + private static {8}[] _data2 = new {8}[Op2ElementCount]; + + private static {5}<{6}> _clsVar1; + private static {7}<{8}> _clsVar2; + + private {5}<{6}> _fld1; + private {7}<{8}> _fld2; + + private HorizontalBinaryOpTest__DataTable<{4}, {6}, {8}> _dataTable; + + static HorizontalBinaryOpTest__{2}{4}() + {{ + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _clsVar1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _clsVar2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize); + }} + + public HorizontalBinaryOpTest__{2}{4}() + {{ + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _fld1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _fld2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }} + for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }} + _dataTable = new HorizontalBinaryOpTest__DataTable<{4}, {6}, {8}>(_data1, _data2, new {4}[RetElementCount], VectorSize); + }} + + public bool IsSupported => {0}.IsSupported; + + public bool Succeeded {{ get; set; }} + + public void RunBasicScenario_UnsafeRead() + {{ + var result = {0}.{2}( + Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunBasicScenario_Load() + {{ + var result = {0}.{2}( + {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunBasicScenario_LoadAligned() + {{ + var result = {0}.{2}( + {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunReflectionScenario_UnsafeRead() + {{ + var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }}) + .Invoke(null, new object[] {{ + Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr) + }}); + + Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunReflectionScenario_Load() + {{ + var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }}) + .Invoke(null, new object[] {{ + {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr)) + }}); + + Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunReflectionScenario_LoadAligned() + {{ + var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }}) + .Invoke(null, new object[] {{ + {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)), + {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr)) + }}); + + Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + }} + + public void RunClsVarScenario() + {{ + var result = {0}.{2}( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr); + }} + + public void RunLclVarScenario_UnsafeRead() + {{ + var left = Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr); + var right = Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr); + var result = {0}.{2}(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + }} + + public void RunLclVarScenario_Load() + {{ + var left = {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)); + var right = {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr)); + var result = {0}.{2}(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + }} + + public void RunLclVarScenario_LoadAligned() + {{ + var left = {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)); + var right = {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr)); + var result = {0}.{2}(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + }} + + public void RunLclFldScenario() + {{ + var test = new HorizontalBinaryOpTest__{2}{4}(); + var result = {0}.{2}(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + }} + + public void RunFldScenario() + {{ + var result = {0}.{2}(_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({5}<{6}> left, {7}<{8}> right, void* result, [CallerMemberName] string method = "") + {{ + {6}[] inArray1 = new {6}[Op1ElementCount]; + {8}[] inArray2 = new {8}[Op2ElementCount]; + {4}[] outArray = new {4}[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(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 = "") + {{ + {6}[] inArray1 = new {6}[Op1ElementCount]; + {8}[] inArray2 = new {8}[Op2ElementCount]; + {4}[] outArray = new {4}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{6}, byte>(ref inArray1[0]), ref Unsafe.AsRef(left), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{8}, byte>(ref inArray2[0]), ref Unsafe.AsRef(right), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + }} + + private void ValidateResult({6}[] left, {8}[] right, {4}[] result, [CallerMemberName] string method = "") + {{ + for (var outer = 0; outer < (VectorSize / 16); outer++) + {{ + for (var inner = 0; inner < (8 / sizeof({6})); inner++) + {{ + var i1 = (outer * (16 / sizeof({6}))) + inner; + var i2 = i1 + (8 / sizeof({6})); + var i3 = (outer * (16 / sizeof({6}))) + (inner * 2); + + if ({12}) + {{ + Succeeded = false; + break; + }} + + if ({13}) + {{ + Succeeded = false; + break; + }} + }} + }} + + if (!Succeeded) + {{ + Console.WriteLine($"{{nameof({0})}}.{{nameof({0}.{2})}}<{4}>({5}<{6}>, {7}<{8}>): {{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/Shared/HorizontalBinOpTest_DataTable.cs b/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest_DataTable.cs new file mode 100644 index 0000000..eb6af4a --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest_DataTable.cs @@ -0,0 +1,65 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public unsafe struct HorizontalBinaryOpTest__DataTable : IDisposable + where TResult : struct + where TOp1 : struct + where TOp2 : struct + { + private byte[] inArray1; + private byte[] inArray2; + private byte[] outArray; + + private GCHandle inHandle1; + private GCHandle inHandle2; + private GCHandle outHandle; + + private byte simdSize; + + public HorizontalBinaryOpTest__DataTable(TOp1[] inArray1, TOp2[] inArray2, TResult[] outArray, int simdSize) + { + this.inArray1 = new byte[simdSize * 2]; + this.inArray2 = new byte[simdSize * 2]; + this.outArray = new byte[simdSize * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); + + this.simdSize = unchecked((byte)(simdSize)); + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As(ref inArray1[0]), this.simdSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray2Ptr), ref Unsafe.As(ref inArray2[0]), this.simdSize); + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), simdSize); + public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), simdSize); + public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), simdSize); + + public void Dispose() + { + inHandle1.Free(); + inHandle2.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, byte expectedAlignment) + { + // Compute how bad the misalignment is, which is at most (expectedAlignment - 1). + // Then subtract that from the expectedAlignment and add it to the original address + // to compute the aligned address. + + var misalignment = expectedAlignment - ((ulong)(buffer) % expectedAlignment); + return (void*)(buffer + misalignment); + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Double.cs new file mode 100644 index 0000000..420097c --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Double.cs @@ -0,0 +1,329 @@ +// 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 AddSubtractDouble() + { + var test = new AlternatingBinaryOpTest__AddSubtractDouble(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 AlternatingBinaryOpTest__AddSubtractDouble + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Double); + private const int Op2ElementCount = VectorSize / sizeof(Double); + private const int RetElementCount = VectorSize / sizeof(Double); + + private static Double[] _data1 = new Double[Op1ElementCount]; + private static Double[] _data2 = new Double[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private AlternatingBinaryOpTest__DataTable _dataTable; + + static AlternatingBinaryOpTest__AddSubtractDouble() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public AlternatingBinaryOpTest__AddSubtractDouble() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + _dataTable = new AlternatingBinaryOpTest__DataTable(_data1, _data2, new Double[RetElementCount], VectorSize); + } + + public bool IsSupported => Sse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Sse3.AddSubtract( + 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 = Sse3.AddSubtract( + Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Sse3.AddSubtract( + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), 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(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Sse3.AddSubtract( + _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 = Sse3.AddSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)); + var result = Sse3.AddSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)); + var result = Sse3.AddSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new AlternatingBinaryOpTest__AddSubtractDouble(); + var result = Sse3.AddSubtract(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Sse3.AddSubtract(_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 = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + 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 = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + 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(Double[] left, Double[] right, Double[] result, [CallerMemberName] string method = "") + { + for (var i = 0; i < RetElementCount; i += 2) + { + if (BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits(left[i] - right[i])) + { + Succeeded = false; + break; + } + + if (BitConverter.DoubleToInt64Bits(result[i + 1]) != BitConverter.DoubleToInt64Bits(left[i + 1] + right[i + 1])) + { + Succeeded = false; + break; + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.AddSubtract)}(Vector128, Vector128): {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/Sse3/AddSubtract.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Single.cs new file mode 100644 index 0000000..a5a1b4e --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Single.cs @@ -0,0 +1,329 @@ +// 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 AddSubtractSingle() + { + var test = new AlternatingBinaryOpTest__AddSubtractSingle(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse.IsSupported) + { + // 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(); + + if (Sse.IsSupported) + { + // 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(); + + if (Sse.IsSupported) + { + // 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 AlternatingBinaryOpTest__AddSubtractSingle + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Single); + private const int Op2ElementCount = VectorSize / sizeof(Single); + private const int RetElementCount = VectorSize / sizeof(Single); + + private static Single[] _data1 = new Single[Op1ElementCount]; + private static Single[] _data2 = new Single[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private AlternatingBinaryOpTest__DataTable _dataTable; + + static AlternatingBinaryOpTest__AddSubtractSingle() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public AlternatingBinaryOpTest__AddSubtractSingle() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + _dataTable = new AlternatingBinaryOpTest__DataTable(_data1, _data2, new Single[RetElementCount], VectorSize); + } + + public bool IsSupported => Sse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Sse3.AddSubtract( + 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 = Sse3.AddSubtract( + Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Sse3.AddSubtract( + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), 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(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Sse3.AddSubtract( + _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 = Sse3.AddSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)); + var right = Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)); + var result = Sse3.AddSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)); + var right = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)); + var result = Sse3.AddSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new AlternatingBinaryOpTest__AddSubtractSingle(); + var result = Sse3.AddSubtract(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Sse3.AddSubtract(_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 = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + 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 = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + 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(Single[] left, Single[] right, Single[] result, [CallerMemberName] string method = "") + { + for (var i = 0; i < RetElementCount; i += 2) + { + if (BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(left[i] - right[i])) + { + Succeeded = false; + break; + } + + if (BitConverter.SingleToInt32Bits(result[i + 1]) != BitConverter.SingleToInt32Bits(left[i + 1] + right[i + 1])) + { + Succeeded = false; + break; + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.AddSubtract)}(Vector128, Vector128): {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/Sse3/HorizontalAdd.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Double.cs new file mode 100644 index 0000000..ee0c9b3 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Double.cs @@ -0,0 +1,336 @@ +// 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 HorizontalAddDouble() + { + var test = new HorizontalBinaryOpTest__HorizontalAddDouble(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalAddDouble + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Double); + private const int Op2ElementCount = VectorSize / sizeof(Double); + private const int RetElementCount = VectorSize / sizeof(Double); + + private static Double[] _data1 = new Double[Op1ElementCount]; + private static Double[] _data2 = new Double[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalAddDouble() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalAddDouble() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Double[RetElementCount], VectorSize); + } + + public bool IsSupported => Sse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Sse3.HorizontalAdd( + 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 = Sse3.HorizontalAdd( + Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Sse3.HorizontalAdd( + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), 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(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Sse3.HorizontalAdd( + _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 = Sse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalAddDouble(); + var result = Sse3.HorizontalAdd(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Sse3.HorizontalAdd(_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 = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + 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 = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + 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(Double[] left, Double[] right, Double[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Double)); inner++) + { + var i1 = (outer * (16 / sizeof(Double))) + inner; + var i2 = i1 + (8 / sizeof(Double)); + var i3 = (outer * (16 / sizeof(Double))) + (inner * 2); + + if (BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] + left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] + right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalAdd)}(Vector128, Vector128): {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/Sse3/HorizontalAdd.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Single.cs new file mode 100644 index 0000000..ddcd847 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Single.cs @@ -0,0 +1,336 @@ +// 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 HorizontalAddSingle() + { + var test = new HorizontalBinaryOpTest__HorizontalAddSingle(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse.IsSupported) + { + // 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(); + + if (Sse.IsSupported) + { + // 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(); + + if (Sse.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalAddSingle + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Single); + private const int Op2ElementCount = VectorSize / sizeof(Single); + private const int RetElementCount = VectorSize / sizeof(Single); + + private static Single[] _data1 = new Single[Op1ElementCount]; + private static Single[] _data2 = new Single[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalAddSingle() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalAddSingle() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Single[RetElementCount], VectorSize); + } + + public bool IsSupported => Sse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Sse3.HorizontalAdd( + 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 = Sse3.HorizontalAdd( + Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Sse3.HorizontalAdd( + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), 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(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Sse3.HorizontalAdd( + _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 = Sse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)); + var right = Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)); + var right = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalAddSingle(); + var result = Sse3.HorizontalAdd(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Sse3.HorizontalAdd(_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 = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + 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 = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + 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(Single[] left, Single[] right, Single[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Single)); inner++) + { + var i1 = (outer * (16 / sizeof(Single))) + inner; + var i2 = i1 + (8 / sizeof(Single)); + var i3 = (outer * (16 / sizeof(Single))) + (inner * 2); + + if (BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] + left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] + right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalAdd)}(Vector128, Vector128): {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/Sse3/HorizontalSubtract.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Double.cs new file mode 100644 index 0000000..2363341 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Double.cs @@ -0,0 +1,336 @@ +// 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 HorizontalSubtractDouble() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractDouble(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalSubtractDouble + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Double); + private const int Op2ElementCount = VectorSize / sizeof(Double); + private const int RetElementCount = VectorSize / sizeof(Double); + + private static Double[] _data1 = new Double[Op1ElementCount]; + private static Double[] _data2 = new Double[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalSubtractDouble() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalSubtractDouble() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Double[RetElementCount], VectorSize); + } + + public bool IsSupported => Sse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Sse3.HorizontalSubtract( + 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 = Sse3.HorizontalSubtract( + Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Sse3.HorizontalSubtract( + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), 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(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Sse3.HorizontalSubtract( + _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 = Sse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractDouble(); + var result = Sse3.HorizontalSubtract(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Sse3.HorizontalSubtract(_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 = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + 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 = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + 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(Double[] left, Double[] right, Double[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Double)); inner++) + { + var i1 = (outer * (16 / sizeof(Double))) + inner; + var i2 = i1 + (8 / sizeof(Double)); + var i3 = (outer * (16 / sizeof(Double))) + (inner * 2); + + if (BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] - left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] - right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalSubtract)}(Vector128, Vector128): {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/Sse3/HorizontalSubtract.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Single.cs new file mode 100644 index 0000000..1818e3f --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Single.cs @@ -0,0 +1,336 @@ +// 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 HorizontalSubtractSingle() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractSingle(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse.IsSupported) + { + // 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(); + + if (Sse.IsSupported) + { + // 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(); + + if (Sse.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalSubtractSingle + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Single); + private const int Op2ElementCount = VectorSize / sizeof(Single); + private const int RetElementCount = VectorSize / sizeof(Single); + + private static Single[] _data1 = new Single[Op1ElementCount]; + private static Single[] _data2 = new Single[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalSubtractSingle() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalSubtractSingle() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Single[RetElementCount], VectorSize); + } + + public bool IsSupported => Sse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Sse3.HorizontalSubtract( + 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 = Sse3.HorizontalSubtract( + Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Sse3.HorizontalSubtract( + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), 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(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)), + Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Sse3.HorizontalSubtract( + _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 = Sse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)); + var right = Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)); + var right = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr)); + var result = Sse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractSingle(); + var result = Sse3.HorizontalSubtract(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Sse3.HorizontalSubtract(_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 = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + 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 = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + 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(Single[] left, Single[] right, Single[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Single)); inner++) + { + var i1 = (outer * (16 / sizeof(Single))) + inner; + var i2 = i1 + (8 / sizeof(Single)); + var i3 = (outer * (16 / sizeof(Single))) + (inner * 2); + + if (BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] - left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] - right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalSubtract)}(Vector128, Vector128): {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/Sse3/MoveAndDuplicate.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate.cs new file mode 100644 index 0000000..4c843f8 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate.cs @@ -0,0 +1,86 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; + +namespace IntelHardwareIntrinsicTest +{ + class Program + { + const int Pass = 100; + const int Fail = 0; + + static unsafe int Main(string[] args) + { + int testResult = Pass; + + if (Sse3.IsSupported) + { + using (TestTable doubleTable = new TestTable(new double[2] { 1, -5 }, new double[2])) + { + var vf1 = Unsafe.Read>(doubleTable.inArrayPtr); + var vf2 = Sse3.MoveAndDuplicate(vf1); + Unsafe.Write(doubleTable.outArrayPtr, vf2); + + if (BitConverter.DoubleToInt64Bits(doubleTable.inArray[0]) != BitConverter.DoubleToInt64Bits(doubleTable.outArray[0]) || + BitConverter.DoubleToInt64Bits(doubleTable.inArray[0]) != BitConverter.DoubleToInt64Bits(doubleTable.outArray[1])) + { + Console.WriteLine("Sse3 MoveAndDuplicate failed on double:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + } + } + + return testResult; + } + + public unsafe struct TestTable : IDisposable where T : struct + { + public T[] inArray; + public T[] outArray; + + public void* inArrayPtr => inHandle.AddrOfPinnedObject().ToPointer(); + public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer(); + + GCHandle inHandle; + GCHandle outHandle; + public TestTable(T[] a, T[] b) + { + this.inArray = a; + this.outArray = b; + + inHandle = GCHandle.Alloc(inArray, GCHandleType.Pinned); + outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned); + } + public bool CheckResult(Func check) + { + for (int i = 0; i < inArray.Length; i++) + { + if (!check(inArray[i], outArray[i])) + { + return false; + } + } + return true; + } + + public void Dispose() + { + inHandle.Free(); + outHandle.Free(); + } + } + + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_r.csproj new file mode 100644 index 0000000..56f7cbf --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_r.csproj @@ -0,0 +1,34 @@ + + + + + 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/Sse3/MoveAndDuplicate_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_ro.csproj new file mode 100644 index 0000000..c723233 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_ro.csproj @@ -0,0 +1,34 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + None + True + + + + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate.cs new file mode 100644 index 0000000..edee8e9 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate.cs @@ -0,0 +1,88 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; + +namespace IntelHardwareIntrinsicTest +{ + class Program + { + const int Pass = 100; + const int Fail = 0; + + static unsafe int Main(string[] args) + { + int testResult = Pass; + + if (Sse3.IsSupported) + { + using (TestTable floatTable = new TestTable(new float[4] { 1, -5, 100, 0 }, new float[4])) + { + var vf1 = Sse.LoadVector128((float*)(floatTable.inArrayPtr)); + var vf2 = Sse3.MoveHighAndDuplicate(vf1); + Unsafe.Write(floatTable.outArrayPtr, vf2); + + if (BitConverter.SingleToInt32Bits(floatTable.inArray[1]) != BitConverter.SingleToInt32Bits(floatTable.outArray[0]) || + BitConverter.SingleToInt32Bits(floatTable.inArray[1]) != BitConverter.SingleToInt32Bits(floatTable.outArray[1]) || + BitConverter.SingleToInt32Bits(floatTable.inArray[3]) != BitConverter.SingleToInt32Bits(floatTable.outArray[2]) || + BitConverter.SingleToInt32Bits(floatTable.inArray[3]) != BitConverter.SingleToInt32Bits(floatTable.outArray[3])) + { + Console.WriteLine("Sse3 MoveHighAndDuplicate failed on float:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + } + } + + return testResult; + } + + public unsafe struct TestTable : IDisposable where T : struct + { + public T[] inArray; + public T[] outArray; + + public void* inArrayPtr => inHandle.AddrOfPinnedObject().ToPointer(); + public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer(); + + GCHandle inHandle; + GCHandle outHandle; + public TestTable(T[] a, T[] b) + { + this.inArray = a; + this.outArray = b; + + inHandle = GCHandle.Alloc(inArray, GCHandleType.Pinned); + outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned); + } + public bool CheckResult(Func check) + { + for (int i = 0; i < inArray.Length; i++) + { + if (!check(inArray[i], outArray[i])) + { + return false; + } + } + return true; + } + + public void Dispose() + { + inHandle.Free(); + outHandle.Free(); + } + } + + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_r.csproj new file mode 100644 index 0000000..293b6a0 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_r.csproj @@ -0,0 +1,34 @@ + + + + + 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/Sse3/MoveHighAndDuplicate_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_ro.csproj new file mode 100644 index 0000000..5a367be --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_ro.csproj @@ -0,0 +1,34 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + None + True + + + + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate.cs new file mode 100644 index 0000000..ee46de0 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate.cs @@ -0,0 +1,88 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; + +namespace IntelHardwareIntrinsicTest +{ + class Program + { + const int Pass = 100; + const int Fail = 0; + + static unsafe int Main(string[] args) + { + int testResult = Pass; + + if (Sse3.IsSupported) + { + using (TestTable floatTable = new TestTable(new float[4] { 1, -5, 100, 0 }, new float[4])) + { + var vf1 = Sse.LoadVector128((float*)(floatTable.inArrayPtr)); + var vf2 = Sse3.MoveLowAndDuplicate(vf1); + Unsafe.Write(floatTable.outArrayPtr, vf2); + + if (BitConverter.SingleToInt32Bits(floatTable.inArray[0]) != BitConverter.SingleToInt32Bits(floatTable.outArray[0]) || + BitConverter.SingleToInt32Bits(floatTable.inArray[0]) != BitConverter.SingleToInt32Bits(floatTable.outArray[1]) || + BitConverter.SingleToInt32Bits(floatTable.inArray[2]) != BitConverter.SingleToInt32Bits(floatTable.outArray[2]) || + BitConverter.SingleToInt32Bits(floatTable.inArray[2]) != BitConverter.SingleToInt32Bits(floatTable.outArray[3])) + { + Console.WriteLine("Sse3 MoveLowAndDuplicate failed on float:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + } + } + + return testResult; + } + + public unsafe struct TestTable : IDisposable where T : struct + { + public T[] inArray; + public T[] outArray; + + public void* inArrayPtr => inHandle.AddrOfPinnedObject().ToPointer(); + public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer(); + + GCHandle inHandle; + GCHandle outHandle; + public TestTable(T[] a, T[] b) + { + this.inArray = a; + this.outArray = b; + + inHandle = GCHandle.Alloc(inArray, GCHandleType.Pinned); + outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned); + } + public bool CheckResult(Func check) + { + for (int i = 0; i < inArray.Length; i++) + { + if (!check(inArray[i], outArray[i])) + { + return false; + } + } + return true; + } + + public void Dispose() + { + inHandle.Free(); + outHandle.Free(); + } + } + + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_r.csproj new file mode 100644 index 0000000..50d2915 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_r.csproj @@ -0,0 +1,34 @@ + + + + + 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/Sse3/MoveLowAndDuplicate_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_ro.csproj new file mode 100644 index 0000000..3dfda31 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_ro.csproj @@ -0,0 +1,34 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + None + True + + + + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Program.Sse3.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Program.Sse3.cs new file mode 100644 index 0000000..13060f8 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Program.Sse3.cs @@ -0,0 +1,24 @@ +// 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() { + ["AddSubtract.Double"] = AddSubtractDouble, + ["AddSubtract.Single"] = AddSubtractSingle, + ["HorizontalAdd.Double"] = HorizontalAddDouble, + ["HorizontalAdd.Single"] = HorizontalAddSingle, + ["HorizontalSubtract.Double"] = HorizontalSubtractDouble, + ["HorizontalSubtract.Single"] = HorizontalSubtractSingle, + }; + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj new file mode 100644 index 0000000..523c4f5 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj @@ -0,0 +1,43 @@ + + + + + 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/Sse3/Sse3_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_ro.csproj new file mode 100644 index 0000000..9a4933e --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_ro.csproj @@ -0,0 +1,43 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + None + True + + + + + + + + + + + + + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs new file mode 100644 index 0000000..68716f6 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs @@ -0,0 +1,306 @@ +// 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 AbsByte() + { + var test = new SimpleUnaryOpTest__AbsByte(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 SimpleUnaryOpTest__AbsByte + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(SByte); + private const int RetElementCount = VectorSize / sizeof(Byte); + + private static SByte[] _data = new SByte[Op1ElementCount]; + + private static Vector128 _clsVar; + + private Vector128 _fld; + + private SimpleUnaryOpTest__DataTable _dataTable; + + static SimpleUnaryOpTest__AbsByte() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar), ref Unsafe.As(ref _data[0]), VectorSize); + } + + public SimpleUnaryOpTest__AbsByte() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld), ref Unsafe.As(ref _data[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); } + _dataTable = new SimpleUnaryOpTest__DataTable(_data, new Byte[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.Abs( + Unsafe.Read>(_dataTable.inArrayPtr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Ssse3.Abs( + Sse2.LoadVector128((SByte*)(_dataTable.inArrayPtr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.Abs( + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArrayPtr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArrayPtr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((SByte*)(_dataTable.inArrayPtr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArrayPtr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.Abs( + _clsVar + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var firstOp = Unsafe.Read>(_dataTable.inArrayPtr); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var firstOp = Sse2.LoadVector128((SByte*)(_dataTable.inArrayPtr)); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var firstOp = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArrayPtr)); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleUnaryOpTest__AbsByte(); + var result = Ssse3.Abs(test._fld); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.Abs(_fld); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector128 firstOp, void* result, [CallerMemberName] string method = "") + { + SByte[] inArray = new SByte[Op1ElementCount]; + Byte[] outArray = new Byte[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + SByte[] inArray = new SByte[Op1ElementCount]; + Byte[] outArray = new Byte[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray[0]), ref Unsafe.AsRef(firstOp), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(SByte[] firstOp, Byte[] result, [CallerMemberName] string method = "") + { + if (result[0] != Math.Abs(firstOp[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != Math.Abs(firstOp[i])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Abs)}(Vector128): {method} failed:"); + Console.WriteLine($" firstOp: ({string.Join(", ", firstOp)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs new file mode 100644 index 0000000..8aad8e8 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs @@ -0,0 +1,306 @@ +// 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 AbsUInt16() + { + var test = new SimpleUnaryOpTest__AbsUInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 SimpleUnaryOpTest__AbsUInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int16); + private const int RetElementCount = VectorSize / sizeof(UInt16); + + private static Int16[] _data = new Int16[Op1ElementCount]; + + private static Vector128 _clsVar; + + private Vector128 _fld; + + private SimpleUnaryOpTest__DataTable _dataTable; + + static SimpleUnaryOpTest__AbsUInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar), ref Unsafe.As(ref _data[0]), VectorSize); + } + + public SimpleUnaryOpTest__AbsUInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld), ref Unsafe.As(ref _data[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); } + _dataTable = new SimpleUnaryOpTest__DataTable(_data, new UInt16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.Abs( + Unsafe.Read>(_dataTable.inArrayPtr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Ssse3.Abs( + Sse2.LoadVector128((Int16*)(_dataTable.inArrayPtr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.Abs( + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArrayPtr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArrayPtr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int16*)(_dataTable.inArrayPtr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArrayPtr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.Abs( + _clsVar + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var firstOp = Unsafe.Read>(_dataTable.inArrayPtr); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var firstOp = Sse2.LoadVector128((Int16*)(_dataTable.inArrayPtr)); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var firstOp = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArrayPtr)); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleUnaryOpTest__AbsUInt16(); + var result = Ssse3.Abs(test._fld); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.Abs(_fld); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector128 firstOp, void* result, [CallerMemberName] string method = "") + { + Int16[] inArray = new Int16[Op1ElementCount]; + UInt16[] outArray = new UInt16[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + Int16[] inArray = new Int16[Op1ElementCount]; + UInt16[] outArray = new UInt16[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray[0]), ref Unsafe.AsRef(firstOp), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(Int16[] firstOp, UInt16[] result, [CallerMemberName] string method = "") + { + if (result[0] != Math.Abs(firstOp[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != Math.Abs(firstOp[i])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Abs)}(Vector128): {method} failed:"); + Console.WriteLine($" firstOp: ({string.Join(", ", firstOp)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs new file mode 100644 index 0000000..7807e01 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs @@ -0,0 +1,306 @@ +// 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 AbsUInt32() + { + var test = new SimpleUnaryOpTest__AbsUInt32(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 SimpleUnaryOpTest__AbsUInt32 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int32); + private const int RetElementCount = VectorSize / sizeof(UInt32); + + private static Int32[] _data = new Int32[Op1ElementCount]; + + private static Vector128 _clsVar; + + private Vector128 _fld; + + private SimpleUnaryOpTest__DataTable _dataTable; + + static SimpleUnaryOpTest__AbsUInt32() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar), ref Unsafe.As(ref _data[0]), VectorSize); + } + + public SimpleUnaryOpTest__AbsUInt32() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld), ref Unsafe.As(ref _data[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); } + _dataTable = new SimpleUnaryOpTest__DataTable(_data, new UInt32[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.Abs( + Unsafe.Read>(_dataTable.inArrayPtr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Ssse3.Abs( + Sse2.LoadVector128((Int32*)(_dataTable.inArrayPtr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.Abs( + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArrayPtr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArrayPtr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int32*)(_dataTable.inArrayPtr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArrayPtr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.Abs( + _clsVar + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var firstOp = Unsafe.Read>(_dataTable.inArrayPtr); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var firstOp = Sse2.LoadVector128((Int32*)(_dataTable.inArrayPtr)); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var firstOp = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArrayPtr)); + var result = Ssse3.Abs(firstOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleUnaryOpTest__AbsUInt32(); + var result = Ssse3.Abs(test._fld); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.Abs(_fld); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector128 firstOp, void* result, [CallerMemberName] string method = "") + { + Int32[] inArray = new Int32[Op1ElementCount]; + UInt32[] outArray = new UInt32[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + Int32[] inArray = new Int32[Op1ElementCount]; + UInt32[] outArray = new UInt32[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray[0]), ref Unsafe.AsRef(firstOp), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(Int32[] firstOp, UInt32[] result, [CallerMemberName] string method = "") + { + if (result[0] != Math.Abs(firstOp[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != Math.Abs(firstOp[i])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Abs)}(Vector128): {method} failed:"); + Console.WriteLine($" firstOp: ({string.Join(", ", firstOp)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs new file mode 100644 index 0000000..ef075eb --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs @@ -0,0 +1,156 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; + +namespace IntelHardwareIntrinsicTest +{ + class Program + { + const int Pass = 100; + const int Fail = 0; + + static unsafe int Main(string[] args) + { + int testResult = Pass; + + if (Ssse3.IsSupported) + { + using (TestTable sbyteTable = new TestTable(new sbyte[16] { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, new sbyte[16] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, new sbyte[16])) + { + var vf1 = Unsafe.Read>(sbyteTable.inArray1Ptr); + var vf2 = Unsafe.Read>(sbyteTable.inArray2Ptr); + + var vf3 = Ssse3.AlignRight(vf1, vf2, 27); + Unsafe.Write(sbyteTable.outArrayPtr, vf3); + + if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 27) && (z[01] == 28) && (z[02] == 29) && (z[03] == 30) && + (z[04] == 31) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) && + (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && + (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00))) + { + Console.WriteLine("SSE AlignRight failed on sbyte:"); + foreach (var item in sbyteTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + + vf3 = Ssse3.AlignRight(vf1, vf2, 5); + Unsafe.Write(sbyteTable.outArrayPtr, vf3); + + if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 05) && (z[01] == 06) && (z[02] == 07) && (z[03] == 08) && + (z[04] == 09) && (z[05] == 10) && (z[06] == 11) && (z[07] == 12) && + (z[08] == 13) && (z[09] == 14) && (z[10] == 15) && (z[11] == 16) && + (z[12] == 17) && (z[13] == 18) && (z[14] == 19) && (z[15] == 20))) + { + Console.WriteLine("SSE AlignRight failed on sbyte:"); + foreach (var item in sbyteTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + + vf3 = Ssse3.AlignRight(vf1, vf2, 250); + Unsafe.Write(sbyteTable.outArrayPtr, vf3); + + if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 00) && (z[01] == 00) && (z[02] == 00) && (z[03] == 00) && + (z[04] == 00) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) && + (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && + (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00))) + { + Console.WriteLine("SSE AlignRight failed on sbyte:"); + foreach (var item in sbyteTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + + vf3 = Ssse3.AlignRight(vf1, vf2, 228); + Unsafe.Write(sbyteTable.outArrayPtr, vf3); + + if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 00) && (z[01] == 00) && (z[02] == 00) && (z[03] == 00) && + (z[04] == 00) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) && + (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && + (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00))) + { + Console.WriteLine("SSE AlignRight failed on sbyte:"); + foreach (var item in sbyteTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + + vf3 = (Vector128)typeof(Ssse3).GetMethod(nameof(Ssse3.AlignRight), new Type[] { vf1.GetType(), vf2.GetType(), typeof(byte) }).Invoke(null, new object[] { vf1, vf2, (byte)(27) }); + Unsafe.Write(sbyteTable.outArrayPtr, vf3); + + if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 27) && (z[01] == 28) && (z[02] == 29) && (z[03] == 30) && + (z[04] == 31) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) && + (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && + (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00))) + { + Console.WriteLine("SSE AlignRight failed on sbyte:"); + foreach (var item in sbyteTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + testResult = Fail; + } + } + } + + return testResult; + } + + public unsafe struct TestTable : IDisposable where T : struct + { + public T[] inArray1; + public T[] inArray2; + public T[] outArray; + + public void* inArray1Ptr => inHandle1.AddrOfPinnedObject().ToPointer(); + public void* inArray2Ptr => inHandle2.AddrOfPinnedObject().ToPointer(); + public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer(); + + GCHandle inHandle1; + GCHandle inHandle2; + GCHandle outHandle; + public TestTable(T[] a, T[] b, T[] c) + { + this.inArray1 = a; + this.inArray2 = b; + this.outArray = c; + + inHandle1 = GCHandle.Alloc(inArray1, GCHandleType.Pinned); + inHandle2 = GCHandle.Alloc(inArray2, GCHandleType.Pinned); + outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned); + } + public bool CheckResult(Func check) + { + return check(inArray1, inArray2, outArray); + } + + public void Dispose() + { + inHandle1.Free(); + inHandle2.Free(); + outHandle.Free(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_r.csproj new file mode 100644 index 0000000..7cebb15 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_r.csproj @@ -0,0 +1,34 @@ + + + + + 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/Ssse3/AlignRight_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_ro.csproj new file mode 100644 index 0000000..4472e49 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_ro.csproj @@ -0,0 +1,34 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + None + True + + + + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int16.cs new file mode 100644 index 0000000..8cbc5b9 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int16.cs @@ -0,0 +1,336 @@ +// 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 HorizontalAddInt16() + { + var test = new HorizontalBinaryOpTest__HorizontalAddInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalAddInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int16); + private const int Op2ElementCount = VectorSize / sizeof(Int16); + private const int RetElementCount = VectorSize / sizeof(Int16); + + private static Int16[] _data1 = new Int16[Op1ElementCount]; + private static Int16[] _data2 = new Int16[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalAddInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalAddInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Int16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.HorizontalAdd( + 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 = Ssse3.HorizontalAdd( + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.HorizontalAdd( + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), 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(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.HorizontalAdd( + _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 = Ssse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalAddInt16(); + var result = Ssse3.HorizontalAdd(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.HorizontalAdd(_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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Int16)); inner++) + { + var i1 = (outer * (16 / sizeof(Int16))) + inner; + var i2 = i1 + (8 / sizeof(Int16)); + var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2); + + if (result[i1] != (short)(left[i3] + left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (result[i2] != (short)(right[i3] + right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalAdd)}(Vector128, Vector128): {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/Ssse3/HorizontalAdd.Int32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int32.cs new file mode 100644 index 0000000..dbbbea9 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int32.cs @@ -0,0 +1,336 @@ +// 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 HorizontalAddInt32() + { + var test = new HorizontalBinaryOpTest__HorizontalAddInt32(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalAddInt32 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int32); + private const int Op2ElementCount = VectorSize / sizeof(Int32); + private const int RetElementCount = VectorSize / sizeof(Int32); + + private static Int32[] _data1 = new Int32[Op1ElementCount]; + private static Int32[] _data2 = new Int32[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalAddInt32() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalAddInt32() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Int32[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.HorizontalAdd( + 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 = Ssse3.HorizontalAdd( + Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.HorizontalAdd( + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), 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(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.HorizontalAdd( + _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 = Ssse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalAdd(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalAddInt32(); + var result = Ssse3.HorizontalAdd(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.HorizontalAdd(_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 = "") + { + Int32[] inArray1 = new Int32[Op1ElementCount]; + Int32[] inArray2 = new Int32[Op2ElementCount]; + Int32[] outArray = new Int32[RetElementCount]; + + 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 = "") + { + Int32[] inArray1 = new Int32[Op1ElementCount]; + Int32[] inArray2 = new Int32[Op2ElementCount]; + Int32[] outArray = new Int32[RetElementCount]; + + 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(Int32[] left, Int32[] right, Int32[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Int32)); inner++) + { + var i1 = (outer * (16 / sizeof(Int32))) + inner; + var i2 = i1 + (8 / sizeof(Int32)); + var i3 = (outer * (16 / sizeof(Int32))) + (inner * 2); + + if (result[i1] != (int)(left[i3] + left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (result[i2] != (int)(right[i3] + right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalAdd)}(Vector128, Vector128): {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/Ssse3/HorizontalAddSaturate.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAddSaturate.Int16.cs new file mode 100644 index 0000000..4df9a9c --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAddSaturate.Int16.cs @@ -0,0 +1,336 @@ +// 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 HorizontalAddSaturateInt16() + { + var test = new HorizontalBinaryOpTest__HorizontalAddSaturateInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalAddSaturateInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int16); + private const int Op2ElementCount = VectorSize / sizeof(Int16); + private const int RetElementCount = VectorSize / sizeof(Int16); + + private static Int16[] _data1 = new Int16[Op1ElementCount]; + private static Int16[] _data2 = new Int16[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalAddSaturateInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalAddSaturateInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Int16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.HorizontalAddSaturate( + 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 = Ssse3.HorizontalAddSaturate( + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.HorizontalAddSaturate( + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAddSaturate), 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(Ssse3).GetMethod(nameof(Ssse3.HorizontalAddSaturate), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAddSaturate), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.HorizontalAddSaturate( + _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 = Ssse3.HorizontalAddSaturate(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalAddSaturate(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalAddSaturate(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalAddSaturateInt16(); + var result = Ssse3.HorizontalAddSaturate(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.HorizontalAddSaturate(_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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Int16)); inner++) + { + var i1 = (outer * (16 / sizeof(Int16))) + inner; + var i2 = i1 + (8 / sizeof(Int16)); + var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2); + + if (result[i1] != Math.Clamp((left[i3] + left[i3 + 1]), short.MinValue, short.MaxValue)) + { + Succeeded = false; + break; + } + + if (result[i2] != Math.Clamp((right[i3] + right[i3 + 1]), short.MinValue, short.MaxValue)) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalAddSaturate)}(Vector128, Vector128): {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/Ssse3/HorizontalSubtract.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int16.cs new file mode 100644 index 0000000..457f0ed --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int16.cs @@ -0,0 +1,336 @@ +// 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 HorizontalSubtractInt16() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalSubtractInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int16); + private const int Op2ElementCount = VectorSize / sizeof(Int16); + private const int RetElementCount = VectorSize / sizeof(Int16); + + private static Int16[] _data1 = new Int16[Op1ElementCount]; + private static Int16[] _data2 = new Int16[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalSubtractInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalSubtractInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Int16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.HorizontalSubtract( + 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 = Ssse3.HorizontalSubtract( + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.HorizontalSubtract( + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), 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(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.HorizontalSubtract( + _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 = Ssse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractInt16(); + var result = Ssse3.HorizontalSubtract(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.HorizontalSubtract(_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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Int16)); inner++) + { + var i1 = (outer * (16 / sizeof(Int16))) + inner; + var i2 = i1 + (8 / sizeof(Int16)); + var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2); + + if (result[i1] != (short)(left[i3] - left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (result[i2] != (short)(right[i3] - right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalSubtract)}(Vector128, Vector128): {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/Ssse3/HorizontalSubtract.Int32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int32.cs new file mode 100644 index 0000000..eb20803 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int32.cs @@ -0,0 +1,336 @@ +// 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 HorizontalSubtractInt32() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractInt32(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalSubtractInt32 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int32); + private const int Op2ElementCount = VectorSize / sizeof(Int32); + private const int RetElementCount = VectorSize / sizeof(Int32); + + private static Int32[] _data1 = new Int32[Op1ElementCount]; + private static Int32[] _data2 = new Int32[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalSubtractInt32() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalSubtractInt32() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Int32[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.HorizontalSubtract( + 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 = Ssse3.HorizontalSubtract( + Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.HorizontalSubtract( + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), 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(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.HorizontalSubtract( + _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 = Ssse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalSubtract(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractInt32(); + var result = Ssse3.HorizontalSubtract(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.HorizontalSubtract(_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 = "") + { + Int32[] inArray1 = new Int32[Op1ElementCount]; + Int32[] inArray2 = new Int32[Op2ElementCount]; + Int32[] outArray = new Int32[RetElementCount]; + + 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 = "") + { + Int32[] inArray1 = new Int32[Op1ElementCount]; + Int32[] inArray2 = new Int32[Op2ElementCount]; + Int32[] outArray = new Int32[RetElementCount]; + + 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(Int32[] left, Int32[] right, Int32[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Int32)); inner++) + { + var i1 = (outer * (16 / sizeof(Int32))) + inner; + var i2 = i1 + (8 / sizeof(Int32)); + var i3 = (outer * (16 / sizeof(Int32))) + (inner * 2); + + if (result[i1] != (int)(left[i3] - left[i3 + 1])) + { + Succeeded = false; + break; + } + + if (result[i2] != (int)(right[i3] - right[i3 + 1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalSubtract)}(Vector128, Vector128): {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/Ssse3/HorizontalSubtractSaturate.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtractSaturate.Int16.cs new file mode 100644 index 0000000..369adf3 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtractSaturate.Int16.cs @@ -0,0 +1,336 @@ +// 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 HorizontalSubtractSaturateInt16() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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 HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int16); + private const int Op2ElementCount = VectorSize / sizeof(Int16); + private const int RetElementCount = VectorSize / sizeof(Int16); + + private static Int16[] _data1 = new Int16[Op1ElementCount]; + private static Int16[] _data2 = new Int16[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private HorizontalBinaryOpTest__DataTable _dataTable; + + static HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + _dataTable = new HorizontalBinaryOpTest__DataTable(_data1, _data2, new Int16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.HorizontalSubtractSaturate( + 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 = Ssse3.HorizontalSubtractSaturate( + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.HorizontalSubtractSaturate( + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtractSaturate), 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(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtractSaturate), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtractSaturate), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.HorizontalSubtractSaturate( + _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 = Ssse3.HorizontalSubtractSaturate(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalSubtractSaturate(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.HorizontalSubtractSaturate(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16(); + var result = Ssse3.HorizontalSubtractSaturate(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.HorizontalSubtractSaturate(_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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "") + { + for (var outer = 0; outer < (VectorSize / 16); outer++) + { + for (var inner = 0; inner < (8 / sizeof(Int16)); inner++) + { + var i1 = (outer * (16 / sizeof(Int16))) + inner; + var i2 = i1 + (8 / sizeof(Int16)); + var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2); + + if (result[i1] != Math.Clamp((left[i3] - left[i3 + 1]), short.MinValue, short.MaxValue)) + { + Succeeded = false; + break; + } + + if (result[i2] != Math.Clamp((right[i3] - right[i3 + 1]), short.MinValue, short.MaxValue)) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalSubtractSaturate)}(Vector128, Vector128): {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/Ssse3/MultiplyAddAdjacent.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyAddAdjacent.Int16.cs new file mode 100644 index 0000000..113a594 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyAddAdjacent.Int16.cs @@ -0,0 +1,330 @@ +// 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 MultiplyAddAdjacentInt16() + { + var test = new SimpleBinaryOpTest__MultiplyAddAdjacentInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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__MultiplyAddAdjacentInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Byte); + private const int Op2ElementCount = VectorSize / sizeof(SByte); + private const int RetElementCount = VectorSize / sizeof(Int16); + + private static Byte[] _data1 = new Byte[Op1ElementCount]; + private static SByte[] _data2 = new SByte[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__MultiplyAddAdjacentInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__MultiplyAddAdjacentInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Int16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.MultiplyAddAdjacent( + 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 = Ssse3.MultiplyAddAdjacent( + Sse2.LoadVector128((Byte*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.MultiplyAddAdjacent( + Sse2.LoadAlignedVector128((Byte*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyAddAdjacent), 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(Ssse3).GetMethod(nameof(Ssse3.MultiplyAddAdjacent), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Byte*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyAddAdjacent), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Byte*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.MultiplyAddAdjacent( + _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 = Ssse3.MultiplyAddAdjacent(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Byte*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)); + var result = Ssse3.MultiplyAddAdjacent(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Byte*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)); + var result = Ssse3.MultiplyAddAdjacent(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__MultiplyAddAdjacentInt16(); + var result = Ssse3.MultiplyAddAdjacent(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.MultiplyAddAdjacent(_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 = "") + { + Byte[] inArray1 = new Byte[Op1ElementCount]; + SByte[] inArray2 = new SByte[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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 = "") + { + Byte[] inArray1 = new Byte[Op1ElementCount]; + SByte[] inArray2 = new SByte[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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(Byte[] left, SByte[] right, Int16[] result, [CallerMemberName] string method = "") + { + if (result[0] != Math.Clamp(((right[1] * left[1]) + (right[0] * left[0])), short.MinValue, short.MaxValue)) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != Math.Clamp(((right[(i * 2) + 1] * left[(i * 2) + 1]) + (right[i * 2] * left[i * 2])), short.MinValue, short.MaxValue)) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.MultiplyAddAdjacent)}(Vector128, Vector128): {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/Ssse3/MultiplyHighRoundScale.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyHighRoundScale.Int16.cs new file mode 100644 index 0000000..1c71d91 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyHighRoundScale.Int16.cs @@ -0,0 +1,330 @@ +// 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 MultiplyHighRoundScaleInt16() + { + var test = new SimpleBinaryOpTest__MultiplyHighRoundScaleInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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__MultiplyHighRoundScaleInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int16); + private const int Op2ElementCount = VectorSize / sizeof(Int16); + private const int RetElementCount = VectorSize / sizeof(Int16); + + private static Int16[] _data1 = new Int16[Op1ElementCount]; + private static Int16[] _data2 = new Int16[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__MultiplyHighRoundScaleInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__MultiplyHighRoundScaleInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Int16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.MultiplyHighRoundScale( + 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 = Ssse3.MultiplyHighRoundScale( + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.MultiplyHighRoundScale( + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyHighRoundScale), 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(Ssse3).GetMethod(nameof(Ssse3.MultiplyHighRoundScale), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyHighRoundScale), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.MultiplyHighRoundScale( + _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 = Ssse3.MultiplyHighRoundScale(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.MultiplyHighRoundScale(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.MultiplyHighRoundScale(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__MultiplyHighRoundScaleInt16(); + var result = Ssse3.MultiplyHighRoundScale(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.MultiplyHighRoundScale(_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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "") + { + if (result[0] != (short)((((left[0] * right[0]) >> 14) + 1) >> 1)) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != (short)((((left[i] * right[i]) >> 14) + 1) >> 1)) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.MultiplyHighRoundScale)}(Vector128, Vector128): {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/Ssse3/Program.Ssse3.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Program.Ssse3.cs new file mode 100644 index 0000000..bb22c09 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Program.Ssse3.cs @@ -0,0 +1,33 @@ +// 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() { + ["Abs.Byte"] = AbsByte, + ["Abs.UInt16"] = AbsUInt16, + ["Abs.UInt32"] = AbsUInt32, + ["HorizontalAdd.Int16"] = HorizontalAddInt16, + ["HorizontalAdd.Int32"] = HorizontalAddInt32, + ["HorizontalAddSaturate.Int16"] = HorizontalAddSaturateInt16, + ["HorizontalSubtract.Int16"] = HorizontalSubtractInt16, + ["HorizontalSubtract.Int32"] = HorizontalSubtractInt32, + ["HorizontalSubtractSaturate.Int16"] = HorizontalSubtractSaturateInt16, + ["MultiplyAddAdjacent.Int16"] = MultiplyAddAdjacentInt16, + ["MultiplyHighRoundScale.Int16"] = MultiplyHighRoundScaleInt16, + ["Shuffle.SByte"] = ShuffleSByte, + ["Sign.SByte"] = SignSByte, + ["Sign.Int16"] = SignInt16, + ["Sign.Int32"] = SignInt32, + }; + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs new file mode 100644 index 0000000..877c5bb --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs @@ -0,0 +1,330 @@ +// 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 ShuffleSByte() + { + var test = new SimpleBinaryOpTest__ShuffleSByte(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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__ShuffleSByte + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(SByte); + private const int Op2ElementCount = VectorSize / sizeof(SByte); + private const int RetElementCount = VectorSize / sizeof(SByte); + + private static SByte[] _data1 = new SByte[Op1ElementCount]; + private static SByte[] _data2 = new SByte[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__ShuffleSByte() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__ShuffleSByte() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new SByte[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.Shuffle( + 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 = Ssse3.Shuffle( + Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.Shuffle( + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), 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(Ssse3).GetMethod(nameof(Ssse3.Shuffle), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.Shuffle( + _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 = Ssse3.Shuffle(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Shuffle(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Shuffle(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__ShuffleSByte(); + var result = Ssse3.Shuffle(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.Shuffle(_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 = "") + { + SByte[] inArray1 = new SByte[Op1ElementCount]; + SByte[] inArray2 = new SByte[Op2ElementCount]; + SByte[] outArray = new SByte[RetElementCount]; + + 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 = "") + { + SByte[] inArray1 = new SByte[Op1ElementCount]; + SByte[] inArray2 = new SByte[Op2ElementCount]; + SByte[] outArray = new SByte[RetElementCount]; + + 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(SByte[] left, SByte[] right, SByte[] result, [CallerMemberName] string method = "") + { + if (result[0] != ((right[0] < 0) ? 0 : left[right[0] & 0x0F])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != ((right[i] < 0) ? 0 : left[right[i] & 0x0F])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Shuffle)}(Vector128, Vector128): {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/Ssse3/Sign.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int16.cs new file mode 100644 index 0000000..2193a3b --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int16.cs @@ -0,0 +1,330 @@ +// 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 SignInt16() + { + var test = new SimpleBinaryOpTest__SignInt16(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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__SignInt16 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int16); + private const int Op2ElementCount = VectorSize / sizeof(Int16); + private const int RetElementCount = VectorSize / sizeof(Int16); + + private static Int16[] _data1 = new Int16[Op1ElementCount]; + private static Int16[] _data2 = new Int16[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__SignInt16() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__SignInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Int16[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.Sign( + 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 = Ssse3.Sign( + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.Sign( + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), 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(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.Sign( + _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 = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__SignInt16(); + var result = Ssse3.Sign(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.Sign(_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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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 = "") + { + Int16[] inArray1 = new Int16[Op1ElementCount]; + Int16[] inArray2 = new Int16[Op2ElementCount]; + Int16[] outArray = new Int16[RetElementCount]; + + 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(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "") + { + if (result[0] != (right[0] < 0 ? (short)(-left[0]) : (right[0] > 0 ? left[0] : 0))) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != (right[i] < 0 ? (short)(-left[i]) : (right[i] > 0 ? left[i] : 0))) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Sign)}(Vector128, Vector128): {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/Ssse3/Sign.Int32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int32.cs new file mode 100644 index 0000000..ab27528 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int32.cs @@ -0,0 +1,330 @@ +// 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 SignInt32() + { + var test = new SimpleBinaryOpTest__SignInt32(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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__SignInt32 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Int32); + private const int Op2ElementCount = VectorSize / sizeof(Int32); + private const int RetElementCount = VectorSize / sizeof(Int32); + + private static Int32[] _data1 = new Int32[Op1ElementCount]; + private static Int32[] _data2 = new Int32[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__SignInt32() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__SignInt32() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Int32[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.Sign( + 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 = Ssse3.Sign( + Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.Sign( + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), 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(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.Sign( + _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 = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__SignInt32(); + var result = Ssse3.Sign(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.Sign(_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 = "") + { + Int32[] inArray1 = new Int32[Op1ElementCount]; + Int32[] inArray2 = new Int32[Op2ElementCount]; + Int32[] outArray = new Int32[RetElementCount]; + + 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 = "") + { + Int32[] inArray1 = new Int32[Op1ElementCount]; + Int32[] inArray2 = new Int32[Op2ElementCount]; + Int32[] outArray = new Int32[RetElementCount]; + + 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(Int32[] left, Int32[] right, Int32[] result, [CallerMemberName] string method = "") + { + if (result[0] != (right[0] < 0 ? (int)(-left[0]) : (right[0] > 0 ? left[0] : 0))) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != (right[i] < 0 ? (int)(-left[i]) : (right[i] > 0 ? left[i] : 0))) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Sign)}(Vector128, Vector128): {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/Ssse3/Sign.SByte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.SByte.cs new file mode 100644 index 0000000..9947f0e --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.SByte.cs @@ -0,0 +1,330 @@ +// 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 SignSByte() + { + var test = new SimpleBinaryOpTest__SignSByte(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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(); + + if (Sse2.IsSupported) + { + // 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__SignSByte + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(SByte); + private const int Op2ElementCount = VectorSize / sizeof(SByte); + private const int RetElementCount = VectorSize / sizeof(SByte); + + private static SByte[] _data1 = new SByte[Op1ElementCount]; + private static SByte[] _data2 = new SByte[Op2ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__SignSByte() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__SignSByte() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new SByte[RetElementCount], VectorSize); + } + + public bool IsSupported => Ssse3.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Ssse3.Sign( + 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 = Ssse3.Sign( + Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Ssse3.Sign( + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), 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(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128), typeof(Vector128) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)), + Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Ssse3.Sign( + _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 = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)); + var right = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr)); + var result = Ssse3.Sign(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__SignSByte(); + var result = Ssse3.Sign(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Ssse3.Sign(_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 = "") + { + SByte[] inArray1 = new SByte[Op1ElementCount]; + SByte[] inArray2 = new SByte[Op2ElementCount]; + SByte[] outArray = new SByte[RetElementCount]; + + 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 = "") + { + SByte[] inArray1 = new SByte[Op1ElementCount]; + SByte[] inArray2 = new SByte[Op2ElementCount]; + SByte[] outArray = new SByte[RetElementCount]; + + 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(SByte[] left, SByte[] right, SByte[] result, [CallerMemberName] string method = "") + { + if (result[0] != (right[0] < 0 ? (sbyte)(-left[0]) : (right[0] > 0 ? left[0] : 0))) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (result[i] != (right[i] < 0 ? (sbyte)(-left[i]) : (right[i] > 0 ? left[i] : 0))) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Sign)}(Vector128, Vector128): {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/Ssse3/Ssse3_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_r.csproj new file mode 100644 index 0000000..fc18de2 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_r.csproj @@ -0,0 +1,53 @@ + + + + + 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/Ssse3/Ssse3_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_ro.csproj new file mode 100644 index 0000000..8bad57d --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_ro.csproj @@ -0,0 +1,53 @@ + + + + + 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