diff options
author | Fei <fei.peng@intel.com> | 2018-03-12 18:56:51 -0800 |
---|---|---|
committer | Fei <fei.peng@intel.com> | 2018-03-12 18:56:51 -0800 |
commit | ba6b214a7a23c02f4134cac2e700ea984c7c591f (patch) | |
tree | d7a821d3379e09da05166c554c45d7cfc40fbd6b | |
parent | 66d2738ea96fcce753dec1370e79a0c78f7b6adb (diff) | |
download | coreclr-ba6b214a7a23c02f4134cac2e700ea984c7c591f.tar.gz coreclr-ba6b214a7a23c02f4134cac2e700ea984c7c591f.tar.bz2 coreclr-ba6b214a7a23c02f4134cac2e700ea984c7c591f.zip |
Implement AVX Shuffle and Permute intrinsics
13 files changed, 2293 insertions, 1 deletions
diff --git a/src/jit/hwintrinsiclistxarch.h b/src/jit/hwintrinsiclistxarch.h index 9eb87af0d4..c5ce1f2be5 100644 --- a/src/jit/hwintrinsiclistxarch.h +++ b/src/jit/hwintrinsiclistxarch.h @@ -358,6 +358,7 @@ HARDWARE_INTRINSIC(AVX_Max, "Max", HARDWARE_INTRINSIC(AVX_Min, "Min", AVX, -1, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) HARDWARE_INTRINSIC(AVX_Multiply, "Multiply", AVX, -1, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_mulps, INS_mulpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) HARDWARE_INTRINSIC(AVX_Or, "Or", AVX, -1, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_orps, INS_orpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(AVX_Permute, "Permute", AVX, -1, 0, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpermilps, INS_vpermilpd}, HW_Category_IMM, HW_Flag_FullRangeIMM|HW_Flag_UnfixedSIMDSize) HARDWARE_INTRINSIC(AVX_Reciprocal, "Reciprocal", AVX, -1, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_rcpps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX_ReciprocalSqrt, "ReciprocalSqrt", AVX, -1, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_rsqrtps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX_RoundCurrentDirection, "RoundCurrentDirection", AVX, 4, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_roundps, INS_roundpd}, HW_Category_SimpleSIMD, HW_Flag_NoRMWSemantics) @@ -366,6 +367,7 @@ HARDWARE_INTRINSIC(AVX_RoundToNegativeInfinity, "RoundToNeg HARDWARE_INTRINSIC(AVX_RoundToPositiveInfinity, "RoundToPositiveInfinity", AVX, 10, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_roundps, INS_roundpd}, HW_Category_SimpleSIMD, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX_RoundToZero, "RoundToZero", AVX, 11, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_roundps, INS_roundpd}, HW_Category_SimpleSIMD, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX_SetZeroVector256, "SetZeroVector256", AVX, -1, 32, 0, {INS_pxor, INS_pxor, INS_pxor, INS_pxor, INS_pxor, INS_pxor, INS_pxor, INS_pxor, INS_xorps, INS_xorpd}, HW_Category_Helper, HW_Flag_OneTypeGeneric|HW_Flag_NoRMWSemantics) +HARDWARE_INTRINSIC(AVX_Shuffle, "Shuffle", AVX, -1, 32, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_shufps, INS_shufpd}, HW_Category_IMM, HW_Flag_NoRMWSemantics|HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX_Sqrt, "Sqrt", AVX, -1, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sqrtps, INS_sqrtpd}, HW_Category_SimpleSIMD, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX_Store, "Store", AVX, -1, 32, 2, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_MemoryStore, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX_StoreAligned, "StoreAligned", AVX, -1, 32, 2, {INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movaps, INS_movapd}, HW_Category_MemoryStore, HW_Flag_NoRMWSemantics) diff --git a/src/jit/instrsxarch.h b/src/jit/instrsxarch.h index 71da647163..b5b88da982 100644 --- a/src/jit/instrsxarch.h +++ b/src/jit/instrsxarch.h @@ -489,6 +489,9 @@ INST3( vpsrlvq, "psrlvq" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SS INST3( vpsravd, "psravd" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSE38(0x46)) // Variable Bit Shift Right Arithmetic INST3( vpsllvd, "psllvd" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSE38(0x47)) // Variable Bit Shift Left Logical INST3( vpsllvq, "psllvq" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSE38(0x47)) // Variable Bit Shift Left Logical +INST3( vpermilps, "permilps" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSE3A(0x04)) // Permute In-Lane of Quadruples of Single-Precision Floating-Point Values +INST3( vpermilpd, "permilpd" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSE3A(0x05)) // Permute In-Lane of Quadruples of Double-Precision Floating-Point Values + INST3(LAST_AVX_INSTRUCTION, "LAST_AVX_INSTRUCTION", 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, BAD_CODE) // Scalar instructions in SSE4.2 diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj index 93f7b6b179..82a90bdf68 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj @@ -54,6 +54,10 @@ <Compile Include="Multiply.Single.cs" /> <Compile Include="Or.Double.cs" /> <Compile Include="Or.Single.cs" /> + <Compile Include="Permute.Single.1.cs" /> + <Compile Include="Permute.Double.1.cs" /> + <Compile Include="Permute.Single.2.cs" /> + <Compile Include="Permute.Double.2.cs" /> <Compile Include="RoundCurrentDirection.Double.cs" /> <Compile Include="RoundCurrentDirection.Single.cs" /> <Compile Include="RoundToNearestInteger.Double.cs" /> @@ -64,6 +68,8 @@ <Compile Include="RoundToPositiveInfinity.Single.cs" /> <Compile Include="RoundToZero.Double.cs" /> <Compile Include="RoundToZero.Single.cs" /> + <Compile Include="Shuffle.Single.1.cs" /> + <Compile Include="Shuffle.Double.1.cs" /> <Compile Include="Subtract.Double.cs" /> <Compile Include="Subtract.Single.cs" /> <Compile Include="TestC.Byte.cs" /> diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj index 03150f7626..2af1d82afd 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj @@ -54,6 +54,10 @@ <Compile Include="Multiply.Single.cs" /> <Compile Include="Or.Double.cs" /> <Compile Include="Or.Single.cs" /> + <Compile Include="Permute.Single.1.cs" /> + <Compile Include="Permute.Double.1.cs" /> + <Compile Include="Permute.Single.2.cs" /> + <Compile Include="Permute.Double.2.cs" /> <Compile Include="RoundCurrentDirection.Double.cs" /> <Compile Include="RoundCurrentDirection.Single.cs" /> <Compile Include="RoundToNearestInteger.Double.cs" /> @@ -64,6 +68,8 @@ <Compile Include="RoundToPositiveInfinity.Single.cs" /> <Compile Include="RoundToZero.Double.cs" /> <Compile Include="RoundToZero.Single.cs" /> + <Compile Include="Shuffle.Single.1.cs" /> + <Compile Include="Shuffle.Double.1.cs" /> <Compile Include="Subtract.Double.cs" /> <Compile Include="Subtract.Single.cs" /> <Compile Include="TestC.Byte.cs" /> diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.1.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.1.cs new file mode 100644 index 0000000000..1abf18d52c --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.1.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + private static void PermuteDouble1() + { + var test = new SimpleUnaryOpTest__PermuteDouble1(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates calling via reflection works, using Load + test.RunReflectionScenario_Load(); + + // Validates calling via reflection works, using LoadAligned + test.RunReflectionScenario_LoadAligned(); + } + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates passing a local works, using Load + test.RunLclVarScenario_Load(); + + // Validates passing a local works, using LoadAligned + test.RunLclVarScenario_LoadAligned(); + } + + // Validates passing the field of a local works + test.RunLclFldScenario(); + + // Validates passing an instance member works + test.RunFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SimpleUnaryOpTest__PermuteDouble1 + { + private const int VectorSize = 32; + + private const int Op1ElementCount = VectorSize / sizeof(Double); + private const int RetElementCount = VectorSize / sizeof(Double); + + private static Double[] _data = new Double[Op1ElementCount]; + + private static Vector256<Double> _clsVar; + + private Vector256<Double> _fld; + + private SimpleUnaryOpTest__DataTable<Double, Double> _dataTable; + + static SimpleUnaryOpTest__PermuteDouble1() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Double>, byte>(ref _clsVar), ref Unsafe.As<Double, byte>(ref _data[0]), VectorSize); + } + + public SimpleUnaryOpTest__PermuteDouble1() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Double>, byte>(ref _fld), ref Unsafe.As<Double, byte>(ref _data[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (double)(random.NextDouble()); } + _dataTable = new SimpleUnaryOpTest__DataTable<Double, Double>(_data, new Double[RetElementCount], VectorSize); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Avx.Permute( + Unsafe.Read<Vector256<Double>>(_dataTable.inArrayPtr), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Avx.Permute( + Avx.LoadVector256((Double*)(_dataTable.inArrayPtr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Avx.Permute( + Avx.LoadAlignedVector256((Double*)(_dataTable.inArrayPtr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector256<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Unsafe.Read<Vector256<Double>>(_dataTable.inArrayPtr), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Double>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector256<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadVector256((Double*)(_dataTable.inArrayPtr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Double>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector256<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadAlignedVector256((Double*)(_dataTable.inArrayPtr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Double>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Avx.Permute( + _clsVar, + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var firstOp = Unsafe.Read<Vector256<Double>>(_dataTable.inArrayPtr); + var result = Avx.Permute(firstOp, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var firstOp = Avx.LoadVector256((Double*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var firstOp = Avx.LoadAlignedVector256((Double*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleUnaryOpTest__PermuteDouble1(); + var result = Avx.Permute(test._fld, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Avx.Permute(_fld, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256<Double> firstOp, void* result, [CallerMemberName] string method = "") + { + Double[] inArray = new Double[Op1ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + Double[] inArray = new Double[Op1ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(Double[] firstOp, Double[] result, [CallerMemberName] string method = "") + { + if (BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(firstOp[1])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (BitConverter.DoubleToInt64Bits(result[2]) != BitConverter.DoubleToInt64Bits(firstOp[2]) || BitConverter.DoubleToInt64Bits(result[2]) != BitConverter.DoubleToInt64Bits(firstOp[2])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Permute)}<Double>(Vector256<Double><9>): {method} failed:"); + Console.WriteLine($" firstOp: ({string.Join(", ", firstOp)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.2.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.2.cs new file mode 100644 index 0000000000..37722b50c3 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.2.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + private static void PermuteDouble2() + { + var test = new SimpleUnaryOpTest__PermuteDouble2(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // Validates calling via reflection works, using Load + test.RunReflectionScenario_Load(); + + // Validates calling via reflection works, using LoadAligned + test.RunReflectionScenario_LoadAligned(); + } + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + if (Sse2.IsSupported) + { + // Validates passing a local works, using Load + test.RunLclVarScenario_Load(); + + // Validates passing a local works, using LoadAligned + test.RunLclVarScenario_LoadAligned(); + } + + // Validates passing the field of a local works + test.RunLclFldScenario(); + + // Validates passing an instance member works + test.RunFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SimpleUnaryOpTest__PermuteDouble2 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Double); + private const int RetElementCount = VectorSize / sizeof(Double); + + private static Double[] _data = new Double[Op1ElementCount]; + + private static Vector128<Double> _clsVar; + + private Vector128<Double> _fld; + + private SimpleUnaryOpTest__DataTable<Double, Double> _dataTable; + + static SimpleUnaryOpTest__PermuteDouble2() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Double>, byte>(ref _clsVar), ref Unsafe.As<Double, byte>(ref _data[0]), VectorSize); + } + + public SimpleUnaryOpTest__PermuteDouble2() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Double>, byte>(ref _fld), ref Unsafe.As<Double, byte>(ref _data[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (double)(random.NextDouble()); } + _dataTable = new SimpleUnaryOpTest__DataTable<Double, Double>(_data, new Double[RetElementCount], VectorSize); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Avx.Permute( + Unsafe.Read<Vector128<Double>>(_dataTable.inArrayPtr), + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Avx.Permute( + Sse2.LoadVector128((Double*)(_dataTable.inArrayPtr)), + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Avx.Permute( + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArrayPtr)), + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector128<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Unsafe.Read<Vector128<Double>>(_dataTable.inArrayPtr), + (byte)2 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector128<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Sse2.LoadVector128((Double*)(_dataTable.inArrayPtr)), + (byte)2 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector128<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Sse2.LoadAlignedVector128((Double*)(_dataTable.inArrayPtr)), + (byte)2 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Avx.Permute( + _clsVar, + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var firstOp = Unsafe.Read<Vector128<Double>>(_dataTable.inArrayPtr); + var result = Avx.Permute(firstOp, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var firstOp = Sse2.LoadVector128((Double*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var firstOp = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleUnaryOpTest__PermuteDouble2(); + var result = Avx.Permute(test._fld, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Avx.Permute(_fld, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector128<Double> firstOp, void* result, [CallerMemberName] string method = "") + { + Double[] inArray = new Double[Op1ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + Double[] inArray = new Double[Op1ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(Double[] firstOp, Double[] result, [CallerMemberName] string method = "") + { + if (BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(firstOp[0])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (BitConverter.DoubleToInt64Bits(result[1]) != BitConverter.DoubleToInt64Bits(firstOp[1])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Permute)}<Double>(Vector128<Double><9>): {method} failed:"); + Console.WriteLine($" firstOp: ({string.Join(", ", firstOp)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.1.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.1.cs new file mode 100644 index 0000000000..69a7466967 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.1.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + private static void PermuteSingle1() + { + var test = new SimpleUnaryOpTest__PermuteSingle1(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates calling via reflection works, using Load + test.RunReflectionScenario_Load(); + + // Validates calling via reflection works, using LoadAligned + test.RunReflectionScenario_LoadAligned(); + } + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates passing a local works, using Load + test.RunLclVarScenario_Load(); + + // Validates passing a local works, using LoadAligned + test.RunLclVarScenario_LoadAligned(); + } + + // Validates passing the field of a local works + test.RunLclFldScenario(); + + // Validates passing an instance member works + test.RunFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SimpleUnaryOpTest__PermuteSingle1 + { + private const int VectorSize = 32; + + private const int Op1ElementCount = VectorSize / sizeof(Single); + private const int RetElementCount = VectorSize / sizeof(Single); + + private static Single[] _data = new Single[Op1ElementCount]; + + private static Vector256<Single> _clsVar; + + private Vector256<Single> _fld; + + private SimpleUnaryOpTest__DataTable<Single, Single> _dataTable; + + static SimpleUnaryOpTest__PermuteSingle1() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Single>, byte>(ref _clsVar), ref Unsafe.As<Single, byte>(ref _data[0]), VectorSize); + } + + public SimpleUnaryOpTest__PermuteSingle1() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Single>, byte>(ref _fld), ref Unsafe.As<Single, byte>(ref _data[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (float)(random.NextDouble()); } + _dataTable = new SimpleUnaryOpTest__DataTable<Single, Single>(_data, new Single[RetElementCount], VectorSize); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Avx.Permute( + Unsafe.Read<Vector256<Single>>(_dataTable.inArrayPtr), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Avx.Permute( + Avx.LoadVector256((Single*)(_dataTable.inArrayPtr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Avx.Permute( + Avx.LoadAlignedVector256((Single*)(_dataTable.inArrayPtr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector256<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Unsafe.Read<Vector256<Single>>(_dataTable.inArrayPtr), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Single>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector256<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadVector256((Single*)(_dataTable.inArrayPtr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Single>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector256<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadAlignedVector256((Single*)(_dataTable.inArrayPtr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Single>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Avx.Permute( + _clsVar, + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var firstOp = Unsafe.Read<Vector256<Single>>(_dataTable.inArrayPtr); + var result = Avx.Permute(firstOp, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var firstOp = Avx.LoadVector256((Single*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var firstOp = Avx.LoadAlignedVector256((Single*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleUnaryOpTest__PermuteSingle1(); + var result = Avx.Permute(test._fld, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Avx.Permute(_fld, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256<Single> firstOp, void* result, [CallerMemberName] string method = "") + { + Single[] inArray = new Single[Op1ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + Single[] inArray = new Single[Op1ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(Single[] firstOp, Single[] result, [CallerMemberName] string method = "") + { + if (BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(firstOp[1])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (BitConverter.SingleToInt32Bits(result[4]) != BitConverter.SingleToInt32Bits(firstOp[5])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Permute)}<Single>(Vector256<Single><9>): {method} failed:"); + Console.WriteLine($" firstOp: ({string.Join(", ", firstOp)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.2.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.2.cs new file mode 100644 index 0000000000..f30023e5e3 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.2.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + private static void PermuteSingle2() + { + var test = new SimpleUnaryOpTest__PermuteSingle2(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Sse.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + if (Sse.IsSupported) + { + // Validates calling via reflection works, using Load + test.RunReflectionScenario_Load(); + + // Validates calling via reflection works, using LoadAligned + test.RunReflectionScenario_LoadAligned(); + } + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + if (Sse.IsSupported) + { + // Validates passing a local works, using Load + test.RunLclVarScenario_Load(); + + // Validates passing a local works, using LoadAligned + test.RunLclVarScenario_LoadAligned(); + } + + // Validates passing the field of a local works + test.RunLclFldScenario(); + + // Validates passing an instance member works + test.RunFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SimpleUnaryOpTest__PermuteSingle2 + { + private const int VectorSize = 16; + + private const int Op1ElementCount = VectorSize / sizeof(Single); + private const int RetElementCount = VectorSize / sizeof(Single); + + private static Single[] _data = new Single[Op1ElementCount]; + + private static Vector128<Single> _clsVar; + + private Vector128<Single> _fld; + + private SimpleUnaryOpTest__DataTable<Single, Single> _dataTable; + + static SimpleUnaryOpTest__PermuteSingle2() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Single>, byte>(ref _clsVar), ref Unsafe.As<Single, byte>(ref _data[0]), VectorSize); + } + + public SimpleUnaryOpTest__PermuteSingle2() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Single>, byte>(ref _fld), ref Unsafe.As<Single, byte>(ref _data[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (float)(random.NextDouble()); } + _dataTable = new SimpleUnaryOpTest__DataTable<Single, Single>(_data, new Single[RetElementCount], VectorSize); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Avx.Permute( + Unsafe.Read<Vector128<Single>>(_dataTable.inArrayPtr), + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Avx.Permute( + Sse.LoadVector128((Single*)(_dataTable.inArrayPtr)), + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Avx.Permute( + Sse.LoadAlignedVector128((Single*)(_dataTable.inArrayPtr)), + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector128<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Unsafe.Read<Vector128<Single>>(_dataTable.inArrayPtr), + (byte)2 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector128<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Sse.LoadVector128((Single*)(_dataTable.inArrayPtr)), + (byte)2 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Permute), new Type[] { typeof(Vector128<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Sse.LoadAlignedVector128((Single*)(_dataTable.inArrayPtr)), + (byte)2 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Avx.Permute( + _clsVar, + 2 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var firstOp = Unsafe.Read<Vector128<Single>>(_dataTable.inArrayPtr); + var result = Avx.Permute(firstOp, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var firstOp = Sse.LoadVector128((Single*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var firstOp = Sse.LoadAlignedVector128((Single*)(_dataTable.inArrayPtr)); + var result = Avx.Permute(firstOp, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleUnaryOpTest__PermuteSingle2(); + var result = Avx.Permute(test._fld, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Avx.Permute(_fld, 2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector128<Single> firstOp, void* result, [CallerMemberName] string method = "") + { + Single[] inArray = new Single[Op1ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + Single[] inArray = new Single[Op1ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult(Single[] firstOp, Single[] result, [CallerMemberName] string method = "") + { + if (BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(firstOp[2])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (BitConverter.SingleToInt32Bits(result[1]) != BitConverter.SingleToInt32Bits(firstOp[0]) || BitConverter.SingleToInt32Bits(result[2]) != BitConverter.SingleToInt32Bits(firstOp[0])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Permute)}<Single>(Vector128<Single><9>): {method} failed:"); + Console.WriteLine($" firstOp: ({string.Join(", ", firstOp)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs index dd5a44f407..dc8a460f18 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs @@ -39,6 +39,10 @@ namespace JIT.HardwareIntrinsics.X86 ["Multiply.Single"] = MultiplySingle, ["Or.Double"] = OrDouble, ["Or.Single"] = OrSingle, + ["Permute.Single.1"] = PermuteSingle1, + ["Permute.Double.1"] = PermuteDouble1, + ["Permute.Single.2"] = PermuteSingle2, + ["Permute.Double.2"] = PermuteDouble2, ["RoundCurrentDirection.Double"] = RoundCurrentDirectionDouble, ["RoundCurrentDirection.Single"] = RoundCurrentDirectionSingle, ["RoundToNearestInteger.Double"] = RoundToNearestIntegerDouble, @@ -49,6 +53,8 @@ namespace JIT.HardwareIntrinsics.X86 ["RoundToPositiveInfinity.Single"] = RoundToPositiveInfinitySingle, ["RoundToZero.Double"] = RoundToZeroDouble, ["RoundToZero.Single"] = RoundToZeroSingle, + ["Shuffle.Single.1"] = ShuffleSingle1, + ["Shuffle.Double.1"] = ShuffleDouble1, ["Subtract.Double"] = SubtractDouble, ["Subtract.Single"] = SubtractSingle, ["TestC.Byte"] = TestCByte, diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Avx/Shuffle.Double.1.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Shuffle.Double.1.cs new file mode 100644 index 0000000000..7577826ec8 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Shuffle.Double.1.cs @@ -0,0 +1,337 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + private static void ShuffleDouble1() + { + var test = new SimpleBinaryOpTest__ShuffleDouble1(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates calling via reflection works, using Load + test.RunReflectionScenario_Load(); + + // Validates calling via reflection works, using LoadAligned + test.RunReflectionScenario_LoadAligned(); + } + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates passing a local works, using Load + test.RunLclVarScenario_Load(); + + // Validates passing a local works, using LoadAligned + test.RunLclVarScenario_LoadAligned(); + } + + // Validates passing the field of a local works + test.RunLclFldScenario(); + + // Validates passing an instance member works + test.RunFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SimpleBinaryOpTest__ShuffleDouble1 + { + private const int VectorSize = 32; + + private const int Op1ElementCount = VectorSize / sizeof(Double); + private const int Op2ElementCount = VectorSize / sizeof(Double); + private const int RetElementCount = VectorSize / sizeof(Double); + + private static Double[] _data1 = new Double[Op1ElementCount]; + private static Double[] _data2 = new Double[Op2ElementCount]; + + private static Vector256<Double> _clsVar1; + private static Vector256<Double> _clsVar2; + + private Vector256<Double> _fld1; + private Vector256<Double> _fld2; + + private SimpleBinaryOpTest__DataTable<Double, Double, Double> _dataTable; + + static SimpleBinaryOpTest__ShuffleDouble1() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Double>, byte>(ref _clsVar1), ref Unsafe.As<Double, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Double>, byte>(ref _clsVar2), ref Unsafe.As<Double, byte>(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__ShuffleDouble1() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Double>, byte>(ref _fld1), ref Unsafe.As<Double, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Double>, byte>(ref _fld2), ref Unsafe.As<Double, byte>(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (double)(random.NextDouble()); } + _dataTable = new SimpleBinaryOpTest__DataTable<Double, Double, Double>(_data1, _data2, new Double[RetElementCount], VectorSize); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Avx.Shuffle( + Unsafe.Read<Vector256<Double>>(_dataTable.inArray1Ptr), + Unsafe.Read<Vector256<Double>>(_dataTable.inArray2Ptr), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Avx.Shuffle( + Avx.LoadVector256((Double*)(_dataTable.inArray1Ptr)), + Avx.LoadVector256((Double*)(_dataTable.inArray2Ptr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Avx.Shuffle( + Avx.LoadAlignedVector256((Double*)(_dataTable.inArray1Ptr)), + Avx.LoadAlignedVector256((Double*)(_dataTable.inArray2Ptr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Shuffle), new Type[] { typeof(Vector256<Double>), typeof(Vector256<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Unsafe.Read<Vector256<Double>>(_dataTable.inArray1Ptr), + Unsafe.Read<Vector256<Double>>(_dataTable.inArray2Ptr), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Double>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Shuffle), new Type[] { typeof(Vector256<Double>), typeof(Vector256<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadVector256((Double*)(_dataTable.inArray1Ptr)), + Avx.LoadVector256((Double*)(_dataTable.inArray2Ptr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Double>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Shuffle), new Type[] { typeof(Vector256<Double>), typeof(Vector256<Double>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadAlignedVector256((Double*)(_dataTable.inArray1Ptr)), + Avx.LoadAlignedVector256((Double*)(_dataTable.inArray2Ptr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Double>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Avx.Shuffle( + _clsVar1, + _clsVar2, + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var left = Unsafe.Read<Vector256<Double>>(_dataTable.inArray1Ptr); + var right = Unsafe.Read<Vector256<Double>>(_dataTable.inArray2Ptr); + var result = Avx.Shuffle(left, right, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Avx.LoadVector256((Double*)(_dataTable.inArray1Ptr)); + var right = Avx.LoadVector256((Double*)(_dataTable.inArray2Ptr)); + var result = Avx.Shuffle(left, right, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Avx.LoadAlignedVector256((Double*)(_dataTable.inArray1Ptr)); + var right = Avx.LoadAlignedVector256((Double*)(_dataTable.inArray2Ptr)); + var result = Avx.Shuffle(left, right, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__ShuffleDouble1(); + var result = Avx.Shuffle(test._fld1, test._fld2, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Avx.Shuffle(_fld1, _fld2, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256<Double> left, Vector256<Double> right, void* result, [CallerMemberName] string method = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult(void* left, void* right, void* result, [CallerMemberName] string method = "") + { + Double[] inArray1 = new Double[Op1ElementCount]; + Double[] inArray2 = new Double[Op2ElementCount]; + Double[] outArray = new Double[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Double, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult(Double[] left, Double[] right, Double[] result, [CallerMemberName] string method = "") + { + if (BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(left[1])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (BitConverter.DoubleToInt64Bits(result[3]) != BitConverter.DoubleToInt64Bits(right[2])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Shuffle)}<Double>(Vector256<Double>.1, Vector256<Double>): {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/Shuffle.Single.1.cs b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Shuffle.Single.1.cs new file mode 100644 index 0000000000..b3fa1cd1a4 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Avx/Shuffle.Single.1.cs @@ -0,0 +1,337 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + private static void ShuffleSingle1() + { + var test = new SimpleBinaryOpTest__ShuffleSingle1(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates calling via reflection works, using Load + test.RunReflectionScenario_Load(); + + // Validates calling via reflection works, using LoadAligned + test.RunReflectionScenario_LoadAligned(); + } + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + if (Avx.IsSupported) + { + // Validates passing a local works, using Load + test.RunLclVarScenario_Load(); + + // Validates passing a local works, using LoadAligned + test.RunLclVarScenario_LoadAligned(); + } + + // Validates passing the field of a local works + test.RunLclFldScenario(); + + // Validates passing an instance member works + test.RunFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SimpleBinaryOpTest__ShuffleSingle1 + { + private const int VectorSize = 32; + + private const int Op1ElementCount = VectorSize / sizeof(Single); + private const int Op2ElementCount = VectorSize / sizeof(Single); + private const int RetElementCount = VectorSize / sizeof(Single); + + private static Single[] _data1 = new Single[Op1ElementCount]; + private static Single[] _data2 = new Single[Op2ElementCount]; + + private static Vector256<Single> _clsVar1; + private static Vector256<Single> _clsVar2; + + private Vector256<Single> _fld1; + private Vector256<Single> _fld2; + + private SimpleBinaryOpTest__DataTable<Single, Single, Single> _dataTable; + + static SimpleBinaryOpTest__ShuffleSingle1() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Single>, byte>(ref _clsVar1), ref Unsafe.As<Single, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Single>, byte>(ref _clsVar2), ref Unsafe.As<Single, byte>(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__ShuffleSingle1() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Single>, byte>(ref _fld1), ref Unsafe.As<Single, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector256<Single>, byte>(ref _fld2), ref Unsafe.As<Single, byte>(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (float)(random.NextDouble()); } + _dataTable = new SimpleBinaryOpTest__DataTable<Single, Single, Single>(_data1, _data2, new Single[RetElementCount], VectorSize); + } + + public bool IsSupported => Avx.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = Avx.Shuffle( + Unsafe.Read<Vector256<Single>>(_dataTable.inArray1Ptr), + Unsafe.Read<Vector256<Single>>(_dataTable.inArray2Ptr), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = Avx.Shuffle( + Avx.LoadVector256((Single*)(_dataTable.inArray1Ptr)), + Avx.LoadVector256((Single*)(_dataTable.inArray2Ptr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = Avx.Shuffle( + Avx.LoadAlignedVector256((Single*)(_dataTable.inArray1Ptr)), + Avx.LoadAlignedVector256((Single*)(_dataTable.inArray2Ptr)), + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Shuffle), new Type[] { typeof(Vector256<Single>), typeof(Vector256<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Unsafe.Read<Vector256<Single>>(_dataTable.inArray1Ptr), + Unsafe.Read<Vector256<Single>>(_dataTable.inArray2Ptr), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Single>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Shuffle), new Type[] { typeof(Vector256<Single>), typeof(Vector256<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadVector256((Single*)(_dataTable.inArray1Ptr)), + Avx.LoadVector256((Single*)(_dataTable.inArray2Ptr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Single>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof(Avx).GetMethod(nameof(Avx.Shuffle), new Type[] { typeof(Vector256<Single>), typeof(Vector256<Single>), typeof(byte) }) + .Invoke(null, new object[] { + Avx.LoadAlignedVector256((Single*)(_dataTable.inArray1Ptr)), + Avx.LoadAlignedVector256((Single*)(_dataTable.inArray2Ptr)), + (byte)1 + }); + + Unsafe.Write(_dataTable.outArrayPtr, (Vector256<Single>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = Avx.Shuffle( + _clsVar1, + _clsVar2, + 1 + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var left = Unsafe.Read<Vector256<Single>>(_dataTable.inArray1Ptr); + var right = Unsafe.Read<Vector256<Single>>(_dataTable.inArray2Ptr); + var result = Avx.Shuffle(left, right, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = Avx.LoadVector256((Single*)(_dataTable.inArray1Ptr)); + var right = Avx.LoadVector256((Single*)(_dataTable.inArray2Ptr)); + var result = Avx.Shuffle(left, right, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = Avx.LoadAlignedVector256((Single*)(_dataTable.inArray1Ptr)); + var right = Avx.LoadAlignedVector256((Single*)(_dataTable.inArray2Ptr)); + var result = Avx.Shuffle(left, right, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__ShuffleSingle1(); + var result = Avx.Shuffle(test._fld1, test._fld2, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = Avx.Shuffle(_fld1, _fld2, 1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult(Vector256<Single> left, Vector256<Single> right, void* result, [CallerMemberName] string method = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult(void* left, void* right, void* result, [CallerMemberName] string method = "") + { + Single[] inArray1 = new Single[Op1ElementCount]; + Single[] inArray2 = new Single[Op2ElementCount]; + Single[] outArray = new Single[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<Single, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult(Single[] left, Single[] right, Single[] result, [CallerMemberName] string method = "") + { + if (BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(left[1])) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if (BitConverter.SingleToInt32Bits(result[7]) != BitConverter.SingleToInt32Bits(right[4])) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof(Avx)}.{nameof(Avx.Shuffle)}<Single>(Vector256<Single>.1, Vector256<Single>): {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/GenerateTests.csx b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx index 40fdbea223..f913dd0994 100644 --- a/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx @@ -404,6 +404,10 @@ private static readonly (string templateFileName, Dictionary<string, string> tem ("SimpleBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Multiply", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Single", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["NextValueOp2"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(left[0] * right[0]) != BitConverter.SingleToInt32Bits(result[0])", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(left[i] * right[i]) != BitConverter.SingleToInt32Bits(result[i])"}), ("SimpleBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Or", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Double", ["VectorSize"] = "32", ["NextValueOp1"] = "(double)(random.NextDouble())", ["NextValueOp2"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "(BitConverter.DoubleToInt64Bits(left[0]) | BitConverter.DoubleToInt64Bits(right[0])) != BitConverter.DoubleToInt64Bits(result[0])", ["ValidateRemainingResults"] = "(BitConverter.DoubleToInt64Bits(left[i]) | BitConverter.DoubleToInt64Bits(right[i])) != BitConverter.DoubleToInt64Bits(result[i])"}), ("SimpleBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Or", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Single", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["NextValueOp2"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "(BitConverter.SingleToInt32Bits(left[0]) | BitConverter.SingleToInt32Bits(right[0])) != BitConverter.SingleToInt32Bits(result[0])", ["ValidateRemainingResults"] = "(BitConverter.SingleToInt32Bits(left[i]) | BitConverter.SingleToInt32Bits(right[i])) != BitConverter.SingleToInt32Bits(result[i])"}), + ("ImmUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Permute", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["Imm"] = "1", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(firstOp[1])", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[4]) != BitConverter.SingleToInt32Bits(firstOp[5])"}), + ("ImmUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Permute", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Double", ["Imm"] = "1", ["VectorSize"] = "32", ["NextValueOp1"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(firstOp[1])", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(result[2]) != BitConverter.DoubleToInt64Bits(firstOp[2]) || BitConverter.DoubleToInt64Bits(result[2]) != BitConverter.DoubleToInt64Bits(firstOp[2])"}), + ("ImmUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Sse", ["Method"] = "Permute", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Imm"] = "2", ["VectorSize"] = "16", ["NextValueOp1"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(firstOp[2])", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[1]) != BitConverter.SingleToInt32Bits(firstOp[0]) || BitConverter.SingleToInt32Bits(result[2]) != BitConverter.SingleToInt32Bits(firstOp[0])"}), + ("ImmUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Sse2",["Method"] = "Permute", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Imm"] = "2", ["VectorSize"] = "16", ["NextValueOp1"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(firstOp[0])", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(result[1]) != BitConverter.DoubleToInt64Bits(firstOp[1])"}), ("SimpleUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "RoundCurrentDirection", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Double", ["VectorSize"] = "32", ["NextValueOp1"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(Math.Round(firstOp[0]))", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits(Math.Round(firstOp[i]))"}), ("SimpleUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "RoundCurrentDirection", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(MathF.Round(firstOp[0]))", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(MathF.Round(firstOp[i]))"}), ("SimpleUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "RoundToNearestInteger", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Double", ["VectorSize"] = "32", ["NextValueOp1"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(Math.Round(firstOp[0], MidpointRounding.AwayFromZero))", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits(Math.Round(firstOp[i], MidpointRounding.AwayFromZero))"}), @@ -414,6 +418,8 @@ private static readonly (string templateFileName, Dictionary<string, string> tem ("SimpleUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "RoundToPositiveInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(MathF.Ceiling(firstOp[0]))", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(MathF.Ceiling(firstOp[i]))"}), ("SimpleUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "RoundToZero", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Double", ["VectorSize"] = "32", ["NextValueOp1"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits((firstOp[0] > 0) ? Math.Floor(firstOp[0]) : Math.Ceiling(firstOp[0]))", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits((firstOp[i] > 0) ? Math.Floor(firstOp[i]) : Math.Ceiling(firstOp[i]))"}), ("SimpleUnOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "RoundToZero", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits((firstOp[0] > 0) ? MathF.Floor(firstOp[0]) : MathF.Ceiling(firstOp[0]))", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits((firstOp[i] > 0) ? MathF.Floor(firstOp[i]) : MathF.Ceiling(firstOp[i]))"}), + ("ImmBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Shuffle", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Single", ["Imm"] = "1", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["NextValueOp2"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(left[1])", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[7]) != BitConverter.SingleToInt32Bits(right[4])"}), + ("ImmBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Shuffle", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Double", ["Imm"] = "1", ["VectorSize"] = "32", ["NextValueOp1"] = "(double)(random.NextDouble())", ["NextValueOp2"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits(left[1])", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(result[3]) != BitConverter.DoubleToInt64Bits(right[2])"}), ("SimpleBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Subtract", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Double", ["VectorSize"] = "32", ["NextValueOp1"] = "(double)(random.NextDouble())", ["NextValueOp2"] = "(double)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(left[0] - right[0]) != BitConverter.DoubleToInt64Bits(result[0])", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(left[i] - right[i]) != BitConverter.DoubleToInt64Bits(result[i])"}), ("SimpleBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "Subtract", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Single", ["VectorSize"] = "32", ["NextValueOp1"] = "(float)(random.NextDouble())", ["NextValueOp2"] = "(float)(random.NextDouble())", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(left[0] - right[0]) != BitConverter.SingleToInt32Bits(result[0])", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(left[i] - right[i]) != BitConverter.SingleToInt32Bits(result[i])"}), ("BooleanBinOpTest.template", new Dictionary<string, string> { ["Isa"] = "Avx", ["LoadIsa"] = "Avx", ["Method"] = "TestC", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector256", ["Op2BaseType"] = "Byte", ["VectorSize"] = "32", ["NextValueOp1"] = "(byte)(random.Next(0, byte.MaxValue))", ["NextValueOp2"] = "(byte)(random.Next(0, byte.MaxValue))", ["ValidateFirstResult"] = "(~left[i] & right[i]) == 0"}), @@ -612,7 +618,7 @@ namespace JIT.HardwareIntrinsics.X86 private static bool isImmTemplate(string name) { return name == "ImmUnOpTest.template" || name == "InsertScalarTest.template" || - name == "ExtractScalarTest.template"; + name == "ExtractScalarTest.template" || name == "ImmBinOpTest.template"; } private static void ProcessInput(StreamWriter testListFile, (string templateFileName, Dictionary<string, string> templateData) input) diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/ImmBinOpTest.template b/tests/src/JIT/HardwareIntrinsics/X86/Shared/ImmBinOpTest.template new file mode 100644 index 0000000000..e24445932a --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/X86/Shared/ImmBinOpTest.template @@ -0,0 +1,337 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + private static void {Method}{RetBaseType}{Imm}() + { + var test = new SimpleBinaryOpTest__{Method}{RetBaseType}{Imm}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates calling via reflection works, using Load + test.RunReflectionScenario_Load(); + + // Validates calling via reflection works, using LoadAligned + test.RunReflectionScenario_LoadAligned(); + } + + // Validates passing a static member works + test.RunClsVarScenario(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates passing a local works, using Load + test.RunLclVarScenario_Load(); + + // Validates passing a local works, using LoadAligned + test.RunLclVarScenario_LoadAligned(); + } + + // Validates passing the field of a local works + test.RunLclFldScenario(); + + // Validates passing an instance member works + test.RunFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SimpleBinaryOpTest__{Method}{RetBaseType}{Imm} + { + private const int VectorSize = {VectorSize}; + + private const int Op1ElementCount = VectorSize / sizeof({Op1BaseType}); + private const int Op2ElementCount = VectorSize / sizeof({Op2BaseType}); + private const int RetElementCount = VectorSize / sizeof({RetBaseType}); + + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + + private static {Op1VectorType}<{Op1BaseType}> _clsVar1; + private static {Op2VectorType}<{Op2BaseType}> _clsVar2; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; + + private SimpleBinaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}, {Op2BaseType}> _dataTable; + + static SimpleBinaryOpTest__{Method}{RetBaseType}{Imm}() + { + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _clsVar1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _clsVar2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), VectorSize); + } + + public SimpleBinaryOpTest__{Method}{RetBaseType}{Imm}() + { + Succeeded = true; + + var random = new Random(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), VectorSize); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), VectorSize); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + _dataTable = new SimpleBinaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}, {Op2BaseType}>(_data1, _data2, new {RetBaseType}[RetElementCount], VectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr), + {Imm} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + {Imm} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + var result = {Isa}.{Method}( + {LoadIsa}.LoadAligned{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.LoadAligned{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + {Imm} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>), typeof(byte) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr), + (byte){Imm} + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>), typeof(byte) }) + .Invoke(null, new object[] { + {LoadIsa}.Load{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + (byte){Imm} + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_LoadAligned() + { + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>), typeof(byte) }) + .Invoke(null, new object[] { + {LoadIsa}.LoadAligned{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.LoadAligned{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + (byte){Imm} + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunClsVarScenario() + { + var result = {Isa}.{Method}( + _clsVar1, + _clsVar2, + {Imm} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + var left = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); + var right = Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr); + var result = {Isa}.{Method}(left, right, {Imm}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_Load() + { + var left = {LoadIsa}.Load{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)); + var right = {LoadIsa}.Load{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)); + var result = {Isa}.{Method}(left, right, {Imm}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_LoadAligned() + { + var left = {LoadIsa}.LoadAligned{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)); + var right = {LoadIsa}.LoadAligned{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)); + var result = {Isa}.{Method}(left, right, {Imm}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(left, right, _dataTable.outArrayPtr); + } + + public void RunLclFldScenario() + { + var test = new SimpleBinaryOpTest__{Method}{RetBaseType}{Imm}(); + var result = {Isa}.{Method}(test._fld1, test._fld2, {Imm}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunFldScenario() + { + var result = {Isa}.{Method}(_fld1, _fld2, {Imm}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + Succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> left, {Op2VectorType}<{Op2BaseType}> right, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left); + Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult(void* left, void* right, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] left, {Op2BaseType}[] right, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + if ({ValidateFirstResult}) + { + Succeeded = false; + } + else + { + for (var i = 1; i < RetElementCount; i++) + { + if ({ValidateRemainingResults}) + { + Succeeded = false; + break; + } + } + } + + if (!Succeeded) + { + Console.WriteLine($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>.{Imm}, {Op2VectorType}<{Op2BaseType}>): {method} failed:"); + Console.WriteLine($" left: ({string.Join(", ", left)})"); + Console.WriteLine($" right: ({string.Join(", ", right)})"); + Console.WriteLine($" result: ({string.Join(", ", result)})"); + Console.WriteLine(); + } + } + } +} |