From 2e3e5e247137f6b736e8ed622b86d62c9f182fea Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 27 Jan 2018 15:14:22 -0800 Subject: [PATCH] Updating the Avx HardwareIntrinsic tests to be generated from a template. --- .../JIT/HardwareIntrinsics/X86/Avx/Add.Double.cs | 209 +++++++++++++++++++++ .../JIT/HardwareIntrinsics/X86/Avx/Add.Single.cs | 209 +++++++++++++++++++++ tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.cs | 137 -------------- .../X86/Avx/{Add_r.csproj => Avx_r.csproj} | 8 +- .../X86/Avx/{Add_ro.csproj => Avx_ro.csproj} | 8 +- .../HardwareIntrinsics/X86/Avx/Multiply.Double.cs | 209 +++++++++++++++++++++ .../HardwareIntrinsics/X86/Avx/Multiply.Single.cs | 209 +++++++++++++++++++++ .../src/JIT/HardwareIntrinsics/X86/Avx/Multiply.cs | 108 ----------- .../HardwareIntrinsics/X86/Avx/Multiply_r.csproj | 34 ---- .../HardwareIntrinsics/X86/Avx/Multiply_ro.csproj | 34 ---- .../JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs | 22 +++ .../X86/Shared/GenerateTests.csx | 10 + 12 files changed, 882 insertions(+), 315 deletions(-) create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.Double.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.Single.cs delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.cs rename tests/src/JIT/HardwareIntrinsics/X86/Avx/{Add_r.csproj => Avx_r.csproj} (84%) rename tests/src/JIT/HardwareIntrinsics/X86/Avx/{Add_ro.csproj => Avx_ro.csproj} (84%) create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.Double.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.Single.cs delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.cs delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_r.csproj delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_ro.csproj create mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.Double.cs new file mode 100644 index 0000000..ea22687 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.Double.cs @@ -0,0 +1,209 @@ +// 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 static partial class Program + { + private static void AddDouble() + { + var test = new SimpleBinaryOpTest__AddDouble(); + + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario(); + + // Validates calling via reflection works + test.RunReflectionScenario(); + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works + test.RunLclVarScenario(); + + // 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__AddDouble + { + private const int VectorSize = 32; + private const int ElementCount = VectorSize / sizeof(Double); + + private static Double[] _data1 = new Double[ElementCount]; + private static Double[] _data2 = new Double[ElementCount]; + + private static Vector256 _clsVar1; + private static Vector256 _clsVar2; + + private Vector256 _fld1; + private Vector256 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddDouble() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddDouble() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); _data2[i] = (double)(random.NextDouble()); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Double[ElementCount]); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Avx.Add( + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunReflectionScenario() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Add), new Type[] { typeof(Vector256), typeof(Vector256) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256)(result)); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Avx.Add( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArray); + } + + public void RunLclVarScenario() + { + var left = Unsafe.Read>(_dataTable.inArray1Ptr); + var right = Unsafe.Read>(_dataTable.inArray2Ptr); + var result = Avx.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddDouble(); + var result = Avx.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Avx.Add(_fld1, _fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArray); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256 left, Vector256 right, Double[] result, [CallerMemberName] string method = "") + { + Double[] inArray1 = new Double[ElementCount]; + Double[] inArray2 = new Double[ElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + + ValidateResult(inArray1, inArray2, result, method); + } + + private void ValidateResult(Double[] left, Double[] right, Double[] result, [CallerMemberName] string method = "") + { + if (BitConverter.DoubleToInt64Bits(left[0] + right[0]) != BitConverter.DoubleToInt64Bits(result[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if (BitConverter.DoubleToInt64Bits(left[i] + right[i]) != BitConverter.DoubleToInt64Bits(result[i])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Add)}: {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/Avx/Add.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.Single.cs new file mode 100644 index 0000000..1534cbb --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.Single.cs @@ -0,0 +1,209 @@ +// 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 static partial class Program + { + private static void AddSingle() + { + var test = new SimpleBinaryOpTest__AddSingle(); + + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario(); + + // Validates calling via reflection works + test.RunReflectionScenario(); + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works + test.RunLclVarScenario(); + + // 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__AddSingle + { + private const int VectorSize = 32; + private const int ElementCount = VectorSize / sizeof(Single); + + private static Single[] _data1 = new Single[ElementCount]; + private static Single[] _data2 = new Single[ElementCount]; + + private static Vector256 _clsVar1; + private static Vector256 _clsVar2; + + private Vector256 _fld1; + private Vector256 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddSingle() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddSingle() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); _data2[i] = (float)(random.NextDouble()); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Single[ElementCount]); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Avx.Add( + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunReflectionScenario() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Add), new Type[] { typeof(Vector256), typeof(Vector256) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256)(result)); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Avx.Add( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArray); + } + + public void RunLclVarScenario() + { + var left = Unsafe.Read>(_dataTable.inArray1Ptr); + var right = Unsafe.Read>(_dataTable.inArray2Ptr); + var result = Avx.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddSingle(); + var result = Avx.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Avx.Add(_fld1, _fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArray); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256 left, Vector256 right, Single[] result, [CallerMemberName] string method = "") + { + Single[] inArray1 = new Single[ElementCount]; + Single[] inArray2 = new Single[ElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + + ValidateResult(inArray1, inArray2, result, method); + } + + private void ValidateResult(Single[] left, Single[] right, Single[] result, [CallerMemberName] string method = "") + { + if (BitConverter.SingleToInt32Bits(left[0] + right[0]) != BitConverter.SingleToInt32Bits(result[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if (BitConverter.SingleToInt32Bits(left[i] + right[i]) != BitConverter.SingleToInt32Bits(result[i])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Add)}: {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/Avx/Add.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.cs deleted file mode 100644 index 47624a0..0000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add.cs +++ /dev/null @@ -1,137 +0,0 @@ -// 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 (Avx.IsSupported) - { - using (TestTable floatTable = new TestTable(new float[8] { 1, -5, 100, 0, 1, -5, 100, 0 }, new float[8] { 22, -1, -50, 0, 22, -1, -50, 0 }, new float[8])) - using (TestTable doubleTable = new TestTable(new double[4] { 1, -5, 100, 0 }, new double[4] { 22, -1, -50, 0 }, new double[4])) - { - var vf1 = Unsafe.Read>(floatTable.inArray1Ptr); - var vf2 = Unsafe.Read>(floatTable.inArray2Ptr); - var vf3 = Avx.Add(vf1, vf2); - Unsafe.Write(floatTable.outArrayPtr, vf3); - - if (!floatTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("AVX Add failed on float:"); - foreach (var item in floatTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vf3 = (Vector256)typeof(Avx).GetMethod(nameof(Avx.Add), new Type[] { vf1.GetType(), vf2.GetType() }).Invoke(null, new object[] { vf1, vf2 }); - Unsafe.Write(floatTable.outArrayPtr, vf3); - - if (!floatTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("AVX Add failed via reflection on float:"); - foreach (var item in floatTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vd1 = Unsafe.Read>(doubleTable.inArray1Ptr); - var vd2 = Unsafe.Read>(doubleTable.inArray2Ptr); - var vd3 = Avx.Add(vd1, vd2); - Unsafe.Write(doubleTable.outArrayPtr, vd3); - - if (!doubleTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("AVX Add failed on double:"); - foreach (var item in doubleTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vd3 = (Vector256)typeof(Avx).GetMethod(nameof(Avx.Add), new Type[] { vd1.GetType(), vd2.GetType() }).Invoke(null, new object[] { vd1, vd2 }); - Unsafe.Write(doubleTable.outArrayPtr, vd3); - - if (!doubleTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("AVX Add failed via reflection 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[] 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) - { - for (int i = 0; i < inArray1.Length; i++) - { - if (!check(inArray1[i], inArray2[i], outArray[i])) - { - return false; - } - } - return true; - } - - public void Dispose() - { - inHandle1.Free(); - inHandle2.Free(); - outHandle.Free(); - } - } - - } -} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj similarity index 84% rename from tests/src/JIT/HardwareIntrinsics/X86/Avx/Add_r.csproj rename to tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj index 376806f..073705b 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add_r.csproj +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj @@ -27,7 +27,13 @@ - + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj similarity index 84% rename from tests/src/JIT/HardwareIntrinsics/X86/Avx/Add_ro.csproj rename to tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj index 0b1b518..c894de5 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Add_ro.csproj +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj @@ -27,7 +27,13 @@ - + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.Double.cs new file mode 100644 index 0000000..6da0feb --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.Double.cs @@ -0,0 +1,209 @@ +// 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 static partial class Program + { + private static void MultiplyDouble() + { + var test = new SimpleBinaryOpTest__MultiplyDouble(); + + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario(); + + // Validates calling via reflection works + test.RunReflectionScenario(); + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works + test.RunLclVarScenario(); + + // 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__MultiplyDouble + { + private const int VectorSize = 32; + private const int ElementCount = VectorSize / sizeof(Double); + + private static Double[] _data1 = new Double[ElementCount]; + private static Double[] _data2 = new Double[ElementCount]; + + private static Vector256 _clsVar1; + private static Vector256 _clsVar2; + + private Vector256 _fld1; + private Vector256 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__MultiplyDouble() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__MultiplyDouble() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); _data2[i] = (double)(random.NextDouble()); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Double[ElementCount]); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Avx.Multiply( + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunReflectionScenario() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Multiply), new Type[] { typeof(Vector256), typeof(Vector256) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256)(result)); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Avx.Multiply( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArray); + } + + public void RunLclVarScenario() + { + var left = Unsafe.Read>(_dataTable.inArray1Ptr); + var right = Unsafe.Read>(_dataTable.inArray2Ptr); + var result = Avx.Multiply(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__MultiplyDouble(); + var result = Avx.Multiply(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Avx.Multiply(_fld1, _fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArray); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256 left, Vector256 right, Double[] result, [CallerMemberName] string method = "") + { + Double[] inArray1 = new Double[ElementCount]; + Double[] inArray2 = new Double[ElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + + ValidateResult(inArray1, inArray2, result, method); + } + + private void ValidateResult(Double[] left, Double[] right, Double[] result, [CallerMemberName] string method = "") + { + if (BitConverter.DoubleToInt64Bits(left[0] * right[0]) != BitConverter.DoubleToInt64Bits(result[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if (BitConverter.DoubleToInt64Bits(left[i] * right[i]) != BitConverter.DoubleToInt64Bits(result[i])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Multiply)}: {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/Avx/Multiply.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.Single.cs new file mode 100644 index 0000000..167f3e7 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.Single.cs @@ -0,0 +1,209 @@ +// 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 static partial class Program + { + private static void MultiplySingle() + { + var test = new SimpleBinaryOpTest__MultiplySingle(); + + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario(); + + // Validates calling via reflection works + test.RunReflectionScenario(); + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works + test.RunLclVarScenario(); + + // 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__MultiplySingle + { + private const int VectorSize = 32; + private const int ElementCount = VectorSize / sizeof(Single); + + private static Single[] _data1 = new Single[ElementCount]; + private static Single[] _data2 = new Single[ElementCount]; + + private static Vector256 _clsVar1; + private static Vector256 _clsVar2; + + private Vector256 _fld1; + private Vector256 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__MultiplySingle() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__MultiplySingle() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); _data2[i] = (float)(random.NextDouble()); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Single[ElementCount]); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Avx.Multiply( + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunReflectionScenario() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Multiply), new Type[] { typeof(Vector256), typeof(Vector256) }) + .Invoke(null, new object[] { + Unsafe.Read>(_dataTable.inArray1Ptr), + Unsafe.Read>(_dataTable.inArray2Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256)(result)); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Avx.Multiply( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArray); + } + + public void RunLclVarScenario() + { + var left = Unsafe.Read>(_dataTable.inArray1Ptr); + var right = Unsafe.Read>(_dataTable.inArray2Ptr); + var result = Avx.Multiply(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__MultiplySingle(); + var result = Avx.Multiply(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Avx.Multiply(_fld1, _fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArray); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256 left, Vector256 right, Single[] result, [CallerMemberName] string method = "") + { + Single[] inArray1 = new Single[ElementCount]; + Single[] inArray2 = new Single[ElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + + ValidateResult(inArray1, inArray2, result, method); + } + + private void ValidateResult(Single[] left, Single[] right, Single[] result, [CallerMemberName] string method = "") + { + if (BitConverter.SingleToInt32Bits(left[0] * right[0]) != BitConverter.SingleToInt32Bits(result[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if (BitConverter.SingleToInt32Bits(left[i] * right[i]) != BitConverter.SingleToInt32Bits(result[i])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Multiply)}: {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/Avx/Multiply.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.cs deleted file mode 100644 index 2c7bf21..0000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply.cs +++ /dev/null @@ -1,108 +0,0 @@ -// 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 (Avx.IsSupported) - { - using (TestTable floatTable = new TestTable(new float[8] { 1, -5, 100, 0, 1, -5, 100, 0 }, new float[8] { 22, -1, -50, 0, 22, -1, -50, 0 }, new float[8])) - using (TestTable doubleTable = new TestTable(new double[4] { 1, -5, 100, 0 }, new double[4] { 22, -1, -50, 0 }, new double[4])) - { - var vf1 = Unsafe.Read>(floatTable.inArray1Ptr); - var vf2 = Unsafe.Read>(floatTable.inArray2Ptr); - var vf3 = Avx.Multiply(vf1, vf2); - Unsafe.Write(floatTable.outArrayPtr, vf3); - - var vd1 = Unsafe.Read>(doubleTable.inArray1Ptr); - var vd2 = Unsafe.Read>(doubleTable.inArray2Ptr); - var vd3 = Avx.Multiply(vd1, vd2); - Unsafe.Write(doubleTable.outArrayPtr, vd3); - - if (!floatTable.CheckResult((x, y, z) => x * y == z)) - { - Console.WriteLine("AVX Multiply failed on float:"); - foreach (var item in floatTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - if (!doubleTable.CheckResult((x, y, z) => x * y == z)) - { - Console.WriteLine("AVX Multiply failed on double:"); - foreach (var item in doubleTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - } - } - return testResult; - } - - public unsafe struct TestTable : IDisposable where T1 : struct where T2 : struct where T3 : struct - { - public T1[] inArray1; - public T2[] inArray2; - public T3[] 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(T1[] a, T2[] b, T3[] 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) - { - for (int i = 0; i < inArray1.Length; i++) - { - if (!check(inArray1[i], inArray2[i], outArray[i])) - { - return false; - } - } - return true; - } - - public void Dispose() - { - inHandle1.Free(); - inHandle2.Free(); - outHandle.Free(); - } - } - - } -} \ No newline at end of file diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_r.csproj deleted file mode 100644 index 7c151fe..0000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_r.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {95DFC527-4DC1-495E-97D7-E94EE1F7140D} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - true - - - - - - - False - - - - None - - - - - - - - - - - \ No newline at end of file diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_ro.csproj deleted file mode 100644 index b6fbea2..0000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Multiply_ro.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {95DFC527-4DC1-495E-97D7-E94EE1F7140D} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - true - - - - - - - False - - - - None - true - - - - - - - - - - \ No newline at end of file diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs new file mode 100644 index 0000000..40b6bf2 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs @@ -0,0 +1,22 @@ +// 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() { + ["Add.Double"] = AddDouble, + ["Add.Single"] = AddSingle, + ["Multiply.Double"] = MultiplyDouble, + ["Multiply.Single"] = MultiplySingle, + }; + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx index ea4545a..ab60f27 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx @@ -22,6 +22,15 @@ private static readonly (string templateFileName, string[] templateData)[] Sse2I ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "UInt64", "Vector128", "16", "(ulong)(random.Next(0, int.MaxValue))", "(ulong)(left[0] + right[0]) != result[0]", "(ulong)(left[i] + right[i]) != result[i]"}), }; +private static readonly (string templateFileName, string[] templateData)[] AvxInputs = new [] +{ + // TemplateName Isa, Method, BaseType, VectorType, VectorSize, NextValue, ValidateFirstResult, ValidateRemainingResults + ("SimpleBinOpTest.template", new string[] { "Avx", "Add", "Double", "Vector256", "32", "(double)(random.NextDouble())", "BitConverter.DoubleToInt64Bits(left[0] + right[0]) != BitConverter.DoubleToInt64Bits(result[0])", "BitConverter.DoubleToInt64Bits(left[i] + right[i]) != BitConverter.DoubleToInt64Bits(result[i])"}), + ("SimpleBinOpTest.template", new string[] { "Avx", "Add", "Single", "Vector256", "32", "(float)(random.NextDouble())", "BitConverter.SingleToInt32Bits(left[0] + right[0]) != BitConverter.SingleToInt32Bits(result[0])", "BitConverter.SingleToInt32Bits(left[i] + right[i]) != BitConverter.SingleToInt32Bits(result[i])"}), + ("SimpleBinOpTest.template", new string[] { "Avx", "Multiply", "Double", "Vector256", "32", "(double)(random.NextDouble())", "BitConverter.DoubleToInt64Bits(left[0] * right[0]) != BitConverter.DoubleToInt64Bits(result[0])", "BitConverter.DoubleToInt64Bits(left[i] * right[i]) != BitConverter.DoubleToInt64Bits(result[i])"}), + ("SimpleBinOpTest.template", new string[] { "Avx", "Multiply", "Single", "Vector256", "32", "(float)(random.NextDouble())", "BitConverter.SingleToInt32Bits(left[0] * right[0]) != BitConverter.SingleToInt32Bits(result[0])", "BitConverter.SingleToInt32Bits(left[i] * right[i]) != BitConverter.SingleToInt32Bits(result[i])"}), +}; + private static void ProcessInputs(string isa, (string templateFileName, string[] templateData)[] inputs) { var testListFileName = Path.Combine("..", isa, $"Program.{isa}.cs"); @@ -74,3 +83,4 @@ private static void ProcessInput(StreamWriter testListFile, (string templateFile } ProcessInputs("Sse2", Sse2Inputs); +ProcessInputs("Avx", AvxInputs); -- 2.7.4