From: Tanner Gooding Date: Sat, 27 Jan 2018 19:23:35 +0000 (-0800) Subject: Updating the Sse2 HardwareIntrinsic tests to be generated from a template. X-Git-Tag: accepted/tizen/unified/20190422.045933~3150^2~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a11bfb27a4e291e1553697629b7c3378bf1702cd;p=platform%2Fupstream%2Fcoreclr.git Updating the Sse2 HardwareIntrinsic tests to be generated from a template. --- diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx new file mode 100644 index 0000000..ea4545a --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx @@ -0,0 +1,76 @@ +// 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.IO; + +// NOTE: This file isn't very robust and makes several assumptions. +// It must be run such that its own directory is the working directory. + +private static readonly (string templateFileName, string[] templateData)[] Sse2Inputs = new [] +{ + // TemplateName Isa, Method, BaseType, VectorType, VectorSize, NextValue, ValidateFirstResult, ValidateRemainingResults + ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "Double", "Vector128", "16", "(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[] { "Sse2", "Add", "Byte", "Vector128", "16", "(byte)(random.Next(0, byte.MaxValue))", "(byte)(left[0] + right[0]) != result[0]", "(byte)(left[i] + right[i]) != result[i]"}), + ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "Int16", "Vector128", "16", "(short)(random.Next(short.MinValue, short.MaxValue))", "(short)(left[0] + right[0]) != result[0]", "(short)(left[i] + right[i]) != result[i]"}), + ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "Int32", "Vector128", "16", "(int)(random.Next(int.MinValue, int.MaxValue))", "(int)(left[0] + right[0]) != result[0]", "(int)(left[i] + right[i]) != result[i]"}), + ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "Int64", "Vector128", "16", "(long)(random.Next(int.MinValue, int.MaxValue))", "(long)(left[0] + right[0]) != result[0]", "(long)(left[i] + right[i]) != result[i]"}), + ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "SByte", "Vector128", "16", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))", "(sbyte)(left[0] + right[0]) != result[0]", "(sbyte)(left[i] + right[i]) != result[i]"}), + ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "UInt16", "Vector128", "16", "(ushort)(random.Next(0, ushort.MaxValue))", "(ushort)(left[0] + right[0]) != result[0]", "(ushort)(left[i] + right[i]) != result[i]"}), + ("SimpleBinOpTest.template", new string[] { "Sse2", "Add", "UInt32", "Vector128", "16", "(uint)(random.Next(0, int.MaxValue))", "(uint)(left[0] + right[0]) != result[0]", "(uint)(left[i] + right[i]) != result[i]"}), + ("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 void ProcessInputs(string isa, (string templateFileName, string[] templateData)[] inputs) +{ + var testListFileName = Path.Combine("..", isa, $"Program.{isa}.cs"); + + using (var testListFile = new StreamWriter(testListFileName, append: false)) + { + testListFile.WriteLine(@"// 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() {"); + + foreach (var input in inputs) + { + ProcessInput(testListFile, input); + } + + testListFile.WriteLine(@" }; + } + } +}"); + } +} + +private static void ProcessInput(StreamWriter testListFile, (string templateFileName, string[] templateData) input) +{ + var testName = $"{input.templateData[1]}.{input.templateData[2]}"; + + // Ex: ["Add.Single"] = AddSingle + testListFile.WriteLine($@" [""{testName}""] = {input.templateData[1]}{input.templateData[2]},"); + + var testFileName = Path.Combine("..", input.templateData[0], $"{testName}.cs"); + var template = File.ReadAllText(input.templateFileName); + + if (input.templateData.Length != 0) + { + template = string.Format(template, input.templateData); + } + + File.WriteAllText(testFileName, template); +} + +ProcessInputs("Sse2", Sse2Inputs); diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/Program.cs b/tests/src/JIT/HardwareIntrinsics/X86/Shared/Program.cs new file mode 100644 index 0000000..abc8e31 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/Program.cs @@ -0,0 +1,81 @@ +// 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 + { + private const int PASS = 100; + private const int FAIL = 0; + + private static readonly IDictionary TestList; + + public static int Main(string[] args) + { + var isPassing = true; + + foreach (string testToRun in GetTestsToRun(args)) + { + Console.WriteLine($"Running {testToRun} test..."); + + try + { + TestList[testToRun].Invoke(); + } + catch (Exception e) + { + Console.WriteLine($"Error: {e.Message}"); + isPassing = false; + } + } + + return isPassing ? PASS : FAIL; + } + + private static ICollection GetTestsToRun(string[] args) + { + var testsToRun = new HashSet(); + + for (var i = 0; i < args.Length; i++) + { + var testName = args[i].ToLowerInvariant(); + + if (testName == "all") + { + break; + } + + if (!TestList.ContainsKey(testName)) + { + PrintUsage(); + } + + testsToRun.Add(testName); + } + + return (testsToRun.Count == 0) ? TestList.Keys : testsToRun; + } + + private static void PrintUsage() + { + Console.WriteLine($@"Usage: +{Environment.GetCommandLineArgs()[0]} [testName] + + [testName]: The name of the function to test. + Defaults to 'all'. + Multiple can be specified. + + Available Test Names:"); + foreach (string testName in TestList.Keys) + { + Console.WriteLine($" {testName}"); + } + + Environment.Exit(FAIL); + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpTest.template b/tests/src/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpTest.template new file mode 100644 index 0000000..b906a5c --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpTest.template @@ -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 {1}{2}() + {{ + var test = new SimpleBinaryOpTest__{1}{2}(); + + 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__{1}{2} + {{ + private const int VectorSize = {4}; + private const int ElementCount = VectorSize / sizeof({2}); + + private static {2}[] _data1 = new {2}[ElementCount]; + private static {2}[] _data2 = new {2}[ElementCount]; + + private static {3}<{2}> _clsVar1; + private static {3}<{2}> _clsVar2; + + private {3}<{2}> _fld1; + private {3}<{2}> _fld2; + + private SimpleBinaryOpTest__DataTable<{2}> _dataTable; + + static SimpleBinaryOpTest__{1}{2}() + {{ + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) {{ _data1[i] = {5}; _data2[i] = {5}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{3}<{2}>, byte>(ref _clsVar1), ref Unsafe.As<{2}, byte>(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{3}<{2}>, byte>(ref _clsVar2), ref Unsafe.As<{2}, byte>(ref _data1[0]), VectorSize); + }} + + public SimpleBinaryOpTest__{1}{2}() + {{ + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) {{ _data1[i] = {5}; _data2[i] = {5}; }} + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{3}<{2}>, byte>(ref _fld1), ref Unsafe.As<{2}, byte>(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{3}<{2}>, byte>(ref _fld2), ref Unsafe.As<{2}, byte>(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) {{ _data1[i] = {5}; _data2[i] = {5}; }} + _dataTable = new SimpleBinaryOpTest__DataTable<{2}>(_data1, _data2, new {2}[ElementCount]); + }} + + public bool IsSupported => {0}.IsSupported; + + public bool Succeeded {{ get; set; }} + + public void RunBasicScenario() + {{ + var result = {0}.{1}( + Unsafe.Read<{3}<{2}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{3}<{2}>>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + }} + + public void RunReflectionScenario() + {{ + var result = typeof({0}).GetMethod(nameof({0}.{1}), new Type[] {{ typeof({3}<{2}>), typeof({3}<{2}>) }}) + .Invoke(null, new object[] {{ + Unsafe.Read<{3}<{2}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{3}<{2}>>(_dataTable.inArray2Ptr) + }}); + + Unsafe.Write(_dataTable.outArrayPtr, ({3}<{2}>)(result)); + ValidateResult(_dataTable.inArray1, _dataTable.inArray2, _dataTable.outArray); + }} + + public void RunClsVarScenario() + {{ + var result = {0}.{1}( + _clsVar1, + _clsVar2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArray); + }} + + public void RunLclVarScenario() + {{ + var left = Unsafe.Read<{3}<{2}>>(_dataTable.inArray1Ptr); + var right = Unsafe.Read<{3}<{2}>>(_dataTable.inArray2Ptr); + var result = {0}.{1}(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + }} + + public void RunLclFldScenario() + {{ + var test = new SimpleBinaryOpTest__{1}{2}(); + var result = {0}.{1}(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + }} + + public void RunFldScenario() + {{ + var result = {0}.{1}(_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({3}<{2}> left, {3}<{2}> right, {2}[] result, [CallerMemberName] string method = "") + {{ + {2}[] inArray1 = new {2}[ElementCount]; + {2}[] inArray2 = new {2}[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({2}[] left, {2}[] right, {2}[] result, [CallerMemberName] string method = "") + {{ + if ({6}) + {{ + Succeeded = false; + }} + else + {{ + for (var i = 1; i < left.Length; i++) + {{ + if ({7}) + {{ + Succeeded = false; + break; + }} + }} + }} + + if (!Succeeded) + {{ + Console.WriteLine($"{{nameof({0})}}.{{nameof({0}.{1})}}<{2}>: {{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/SimpleBinOpTest_DataTable.cs b/tests/src/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpTest_DataTable.cs new file mode 100644 index 0000000..896cbc2 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpTest_DataTable.cs @@ -0,0 +1,45 @@ +// 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 SimpleBinaryOpTest__DataTable : IDisposable where T : struct + { + private GCHandle inHandle1; + private GCHandle inHandle2; + private GCHandle outHandle; + + public T[] inArray1; + public T[] inArray2; + public T[] outArray; + + public SimpleBinaryOpTest__DataTable(T[] inArray1, T[] inArray2, T[] outArray) + { + this.inArray1 = inArray1; + this.inArray2 = inArray2; + this.outArray = outArray; + + this.inHandle1 = GCHandle.Alloc(inArray1, GCHandleType.Pinned); + this.inHandle2 = GCHandle.Alloc(inArray2, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned); + } + + public void* inArray1Ptr => inHandle1.AddrOfPinnedObject().ToPointer(); + public void* inArray2Ptr => inHandle2.AddrOfPinnedObject().ToPointer(); + public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer(); + + public void Dispose() + { + inHandle1.Free(); + inHandle2.Free(); + outHandle.Free(); + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Byte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Byte.cs new file mode 100644 index 0000000..c5aaaaa --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Byte.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 AddByte() + { + var test = new SimpleBinaryOpTest__AddByte(); + + 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__AddByte + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(Byte); + + private static Byte[] _data1 = new Byte[ElementCount]; + private static Byte[] _data2 = new Byte[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddByte() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); _data2[i] = (byte)(random.Next(0, byte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddByte() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); _data2[i] = (byte)(random.Next(0, byte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); _data2[i] = (byte)(random.Next(0, byte.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Byte[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddByte(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, Byte[] result, [CallerMemberName] string method = "") + { + Byte[] inArray1 = new Byte[ElementCount]; + Byte[] inArray2 = new Byte[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(Byte[] left, Byte[] right, Byte[] result, [CallerMemberName] string method = "") + { + if ((byte)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((byte)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Double.cs new file mode 100644 index 0000000..7340087 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/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 = 16; + private const int ElementCount = VectorSize / sizeof(Double); + + private static Double[] _data1 = new Double[ElementCount]; + private static Double[] _data2 = new Double[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _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 => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddDouble(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 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(Sse2)}.{nameof(Sse2.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/Sse2/Add.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Int16.cs new file mode 100644 index 0000000..6612bf5 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Int16.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 AddInt16() + { + var test = new SimpleBinaryOpTest__AddInt16(); + + 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__AddInt16 + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(Int16); + + private static Int16[] _data1 = new Int16[ElementCount]; + private static Int16[] _data2 = new Int16[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddInt16() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Int16[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddInt16(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, Int16[] result, [CallerMemberName] string method = "") + { + Int16[] inArray1 = new Int16[ElementCount]; + Int16[] inArray2 = new Int16[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(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "") + { + if ((short)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((short)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.Int32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Int32.cs new file mode 100644 index 0000000..0318172 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Int32.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 AddInt32() + { + var test = new SimpleBinaryOpTest__AddInt32(); + + 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__AddInt32 + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(Int32); + + private static Int32[] _data1 = new Int32[ElementCount]; + private static Int32[] _data2 = new Int32[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddInt32() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddInt32() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Int32[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddInt32(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, Int32[] result, [CallerMemberName] string method = "") + { + Int32[] inArray1 = new Int32[ElementCount]; + Int32[] inArray2 = new Int32[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(Int32[] left, Int32[] right, Int32[] result, [CallerMemberName] string method = "") + { + if ((int)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((int)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.Int64.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Int64.cs new file mode 100644 index 0000000..9d168f0 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.Int64.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 AddInt64() + { + var test = new SimpleBinaryOpTest__AddInt64(); + + 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__AddInt64 + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(Int64); + + private static Int64[] _data1 = new Int64[ElementCount]; + private static Int64[] _data2 = new Int64[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddInt64() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (long)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (long)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddInt64() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (long)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (long)(random.Next(int.MinValue, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (long)(random.Next(int.MinValue, int.MaxValue)); _data2[i] = (long)(random.Next(int.MinValue, int.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new Int64[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddInt64(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, Int64[] result, [CallerMemberName] string method = "") + { + Int64[] inArray1 = new Int64[ElementCount]; + Int64[] inArray2 = new Int64[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(Int64[] left, Int64[] right, Int64[] result, [CallerMemberName] string method = "") + { + if ((long)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((long)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.SByte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.SByte.cs new file mode 100644 index 0000000..fe37a9a --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.SByte.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 AddSByte() + { + var test = new SimpleBinaryOpTest__AddSByte(); + + 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__AddSByte + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(SByte); + + private static SByte[] _data1 = new SByte[ElementCount]; + private static SByte[] _data2 = new SByte[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddSByte() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddSByte() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new SByte[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddSByte(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, SByte[] result, [CallerMemberName] string method = "") + { + SByte[] inArray1 = new SByte[ElementCount]; + SByte[] inArray2 = new SByte[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(SByte[] left, SByte[] right, SByte[] result, [CallerMemberName] string method = "") + { + if ((sbyte)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((sbyte)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.UInt16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.UInt16.cs new file mode 100644 index 0000000..ea6da58 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.UInt16.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 AddUInt16() + { + var test = new SimpleBinaryOpTest__AddUInt16(); + + 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__AddUInt16 + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(UInt16); + + private static UInt16[] _data1 = new UInt16[ElementCount]; + private static UInt16[] _data2 = new UInt16[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddUInt16() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (ushort)(random.Next(0, ushort.MaxValue)); _data2[i] = (ushort)(random.Next(0, ushort.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddUInt16() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (ushort)(random.Next(0, ushort.MaxValue)); _data2[i] = (ushort)(random.Next(0, ushort.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (ushort)(random.Next(0, ushort.MaxValue)); _data2[i] = (ushort)(random.Next(0, ushort.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new UInt16[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddUInt16(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, UInt16[] result, [CallerMemberName] string method = "") + { + UInt16[] inArray1 = new UInt16[ElementCount]; + UInt16[] inArray2 = new UInt16[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(UInt16[] left, UInt16[] right, UInt16[] result, [CallerMemberName] string method = "") + { + if ((ushort)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((ushort)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.UInt32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.UInt32.cs new file mode 100644 index 0000000..4ad0bff --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.UInt32.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 AddUInt32() + { + var test = new SimpleBinaryOpTest__AddUInt32(); + + 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__AddUInt32 + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(UInt32); + + private static UInt32[] _data1 = new UInt32[ElementCount]; + private static UInt32[] _data2 = new UInt32[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddUInt32() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (uint)(random.Next(0, int.MaxValue)); _data2[i] = (uint)(random.Next(0, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data2[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data1[0]), VectorSize); + } + + public SimpleBinaryOpTest__AddUInt32() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (uint)(random.Next(0, int.MaxValue)); _data2[i] = (uint)(random.Next(0, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), VectorSize); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (uint)(random.Next(0, int.MaxValue)); _data2[i] = (uint)(random.Next(0, int.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new UInt32[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddUInt32(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, UInt32[] result, [CallerMemberName] string method = "") + { + UInt32[] inArray1 = new UInt32[ElementCount]; + UInt32[] inArray2 = new UInt32[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(UInt32[] left, UInt32[] right, UInt32[] result, [CallerMemberName] string method = "") + { + if ((uint)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((uint)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.UInt64.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.UInt64.cs new file mode 100644 index 0000000..96ed081 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.UInt64.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 AddUInt64() + { + var test = new SimpleBinaryOpTest__AddUInt64(); + + 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__AddUInt64 + { + private const int VectorSize = 16; + private const int ElementCount = VectorSize / sizeof(UInt64); + + private static UInt64[] _data1 = new UInt64[ElementCount]; + private static UInt64[] _data2 = new UInt64[ElementCount]; + + private static Vector128 _clsVar1; + private static Vector128 _clsVar2; + + private Vector128 _fld1; + private Vector128 _fld2; + + private SimpleBinaryOpTest__DataTable _dataTable; + + static SimpleBinaryOpTest__AddUInt64() + { + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (ulong)(random.Next(0, int.MaxValue)); _data2[i] = (ulong)(random.Next(0, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, 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__AddUInt64() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < ElementCount; i++) { _data1[i] = (ulong)(random.Next(0, int.MaxValue)); _data2[i] = (ulong)(random.Next(0, int.MaxValue)); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As, 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] = (ulong)(random.Next(0, int.MaxValue)); _data2[i] = (ulong)(random.Next(0, int.MaxValue)); } + _dataTable = new SimpleBinaryOpTest__DataTable(_data1, _data2, new UInt64[ElementCount]); + } + + public bool IsSupported => Sse2.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + var result = Sse2.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(Sse2).GetMethod(nameof(Sse2.Add), 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.inArray1, _dataTable.inArray2, _dataTable.outArray); + } + + public void RunClsVarScenario() + { + var result = Sse2.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 = Sse2.Add(left, right); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArray); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__AddUInt64(); + var result = Sse2.Add(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArray); + } + + public void RunFldScenario() + { + var result = Sse2.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(Vector128 left, Vector128 right, UInt64[] result, [CallerMemberName] string method = "") + { + UInt64[] inArray1 = new UInt64[ElementCount]; + UInt64[] inArray2 = new UInt64[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(UInt64[] left, UInt64[] right, UInt64[] result, [CallerMemberName] string method = "") + { + if ((ulong)(left[0] + right[0]) != result[0]) + { + Succeeded = false; + } + else + { + for (var i = 1; i < left.Length; i++) + { + if ((ulong)(left[i] + right[i]) != result[i]) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Sse2)}.{nameof(Sse2.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/Sse2/Add.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.cs deleted file mode 100644 index 3d4458c..0000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add.cs +++ /dev/null @@ -1,356 +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 (Sse2.IsSupported) - { - using (TestTable doubleTable = new TestTable(new double[2] { 1, -5 }, new double[2] { 22, -1 }, new double[2])) - using (TestTable intTable = new TestTable(new int[4] { 1, -5, 100, 0 }, new int[4] { 22, -1, -50, 0 }, new int[4])) - using (TestTable longTable = new TestTable(new long[2] { 1, -5 }, new long[2] { 22, -1 }, new long[2])) - using (TestTable uintTable = new TestTable(new uint[4] { 1, 5, 100, 0 }, new uint[4] { 22, 1, 50, 0 }, new uint[4])) - using (TestTable ulongTable = new TestTable(new ulong[2] { 1, 5 }, new ulong[2] { 22, 1 }, new ulong[2])) - using (TestTable shortTable = new TestTable(new short[8] { 1, -5, 100, 0, 1, -5, 100, 0 }, new short[8] { 22, -1, -50, 0, 22, -1, -50, 0 }, new short[8])) - using (TestTable ushortTable = new TestTable(new ushort[8] { 1, 5, 100, 0, 1, 5, 100, 0 }, new ushort[8] { 22, 1, 50, 0, 22, 1, 50, 0 }, new ushort[8])) - using (TestTable sbyteTable = new TestTable(new sbyte[16] { 1, -5, 100, 0, 1, -5, 100, 0, 1, -5, 100, 0, 1, -5, 100, 0 }, new sbyte[16] { 22, -1, -50, 0, 22, -1, -50, 0, 22, -1, -50, 0, 22, -1, -50, 0 }, new sbyte[16])) - using (TestTable byteTable = new TestTable(new byte[16] { 1, 5, 100, 0, 1, 5, 100, 0, 1, 5, 100, 0, 1, 5, 100, 0 }, new byte[16] { 22, 1, 50, 0, 22, 1, 50, 0, 22, 1, 50, 0, 22, 1, 50, 0 }, new byte[16])) - { - var vd1 = Unsafe.Read>(doubleTable.inArray1Ptr); - var vd2 = Unsafe.Read>(doubleTable.inArray2Ptr); - var vd3 = Sse2.Add(vd1, vd2); - Unsafe.Write(doubleTable.outArrayPtr, vd3); - - if (!doubleTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on double:"); - foreach (var item in doubleTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vd3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.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("SSE2 Add failed via reflection on double:"); - foreach (var item in doubleTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vi1 = Unsafe.Read>(intTable.inArray1Ptr); - var vi2 = Unsafe.Read>(intTable.inArray2Ptr); - var vi3 = Sse2.Add(vi1, vi2); - Unsafe.Write(intTable.outArrayPtr, vi3); - - if (!intTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on int:"); - foreach (var item in intTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vi3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vi1.GetType(), vi2.GetType() }).Invoke(null, new object[] { vi1, vi2 }); - Unsafe.Write(intTable.outArrayPtr, vi3); - - if (!intTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on int:"); - foreach (var item in intTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vl1 = Unsafe.Read>(longTable.inArray1Ptr); - var vl2 = Unsafe.Read>(longTable.inArray2Ptr); - var vl3 = Sse2.Add(vl1, vl2); - Unsafe.Write(longTable.outArrayPtr, vl3); - - if (!longTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on long:"); - foreach (var item in longTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vl3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vl1.GetType(), vl2.GetType() }).Invoke(null, new object[] { vl1, vl2 }); - Unsafe.Write(longTable.outArrayPtr, vl3); - - if (!longTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on long:"); - foreach (var item in longTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vui1 = Unsafe.Read>(uintTable.inArray1Ptr); - var vui2 = Unsafe.Read>(uintTable.inArray2Ptr); - var vui3 = Sse2.Add(vui1, vui2); - Unsafe.Write(uintTable.outArrayPtr, vui3); - - if (!uintTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on uint:"); - foreach (var item in uintTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vui3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vui1.GetType(), vui2.GetType() }).Invoke(null, new object[] { vui1, vui2 }); - Unsafe.Write(uintTable.outArrayPtr, vui3); - - if (!uintTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on uint:"); - foreach (var item in uintTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vul1 = Unsafe.Read>(ulongTable.inArray1Ptr); - var vul2 = Unsafe.Read>(ulongTable.inArray2Ptr); - var vul3 = Sse2.Add(vul1, vul2); - Unsafe.Write(ulongTable.outArrayPtr, vul3); - - if (!ulongTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on ulong:"); - foreach (var item in ulongTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vul3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vul1.GetType(), vul2.GetType() }).Invoke(null, new object[] { vul1, vul2 }); - Unsafe.Write(ulongTable.outArrayPtr, vul3); - - if (!ulongTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on ulong:"); - foreach (var item in ulongTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vs1 = Unsafe.Read>(shortTable.inArray1Ptr); - var vs2 = Unsafe.Read>(shortTable.inArray2Ptr); - var vs3 = Sse2.Add(vs1, vs2); - Unsafe.Write(shortTable.outArrayPtr, vs3); - - if (!shortTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on short:"); - foreach (var item in shortTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vs3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vs1.GetType(), vs2.GetType() }).Invoke(null, new object[] { vs1, vs2 }); - Unsafe.Write(shortTable.outArrayPtr, vs3); - - if (!shortTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on short:"); - foreach (var item in shortTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vus1 = Unsafe.Read>(ushortTable.inArray1Ptr); - var vus2 = Unsafe.Read>(ushortTable.inArray2Ptr); - var vus3 = Sse2.Add(vus1, vus2); - Unsafe.Write(ushortTable.outArrayPtr, vus3); - - if (!ushortTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on ushort:"); - foreach (var item in ushortTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vus3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vus1.GetType(), vus2.GetType() }).Invoke(null, new object[] { vus1, vus2 }); - Unsafe.Write(ushortTable.outArrayPtr, vus3); - - if (!ushortTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on ushort:"); - foreach (var item in ushortTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vsb1 = Unsafe.Read>(sbyteTable.inArray1Ptr); - var vsb2 = Unsafe.Read>(sbyteTable.inArray2Ptr); - var vsb3 = Sse2.Add(vsb1, vsb2); - Unsafe.Write(sbyteTable.outArrayPtr, vsb3); - - if (!sbyteTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on sbyte:"); - foreach (var item in sbyteTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vsb3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vsb1.GetType(), vsb2.GetType() }).Invoke(null, new object[] { vsb1, vsb2 }); - Unsafe.Write(sbyteTable.outArrayPtr, vsb3); - - if (!sbyteTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on sbyte:"); - foreach (var item in sbyteTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - var vb1 = Unsafe.Read>(byteTable.inArray1Ptr); - var vb2 = Unsafe.Read>(byteTable.inArray2Ptr); - var vb3 = Sse2.Add(vb1, vb2); - Unsafe.Write(byteTable.outArrayPtr, vb3); - - if (!byteTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed on byte:"); - foreach (var item in byteTable.outArray) - { - Console.Write(item + ", "); - } - Console.WriteLine(); - testResult = Fail; - } - - vb3 = (Vector128)typeof(Sse2).GetMethod(nameof(Sse2.Add), new Type[] { vb1.GetType(), vb2.GetType() }).Invoke(null, new object[] { vb1, vb2 }); - Unsafe.Write(byteTable.outArrayPtr, vb3); - - if (!byteTable.CheckResult((x, y, z) => x + y == z)) - { - Console.WriteLine("SSE2 Add failed via reflection on byte:"); - foreach (var item in byteTable.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/Sse2/Program.Sse2.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Program.Sse2.cs new file mode 100644 index 0000000..6994fe6 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Program.Sse2.cs @@ -0,0 +1,27 @@ +// 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.Byte"] = AddByte, + ["Add.Int16"] = AddInt16, + ["Add.Int32"] = AddInt32, + ["Add.Int64"] = AddInt64, + ["Add.SByte"] = AddSByte, + ["Add.UInt16"] = AddUInt16, + ["Add.UInt32"] = AddUInt32, + ["Add.UInt64"] = AddUInt64, + }; + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Sse2_r.csproj similarity index 77% rename from tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add_r.csproj rename to tests/src/JIT/HardwareIntrinsics/X86/Sse2/Sse2_r.csproj index 376806f..f25b490 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add_r.csproj +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Sse2_r.csproj @@ -27,7 +27,18 @@ - + + + + + + + + + + + + diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Sse2_ro.csproj similarity index 77% rename from tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add_ro.csproj rename to tests/src/JIT/HardwareIntrinsics/X86/Sse2/Sse2_ro.csproj index 0b1b518..003a20b 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Add_ro.csproj +++ b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/Sse2_ro.csproj @@ -27,7 +27,18 @@ - + + + + + + + + + + + +