summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFei <fei.peng@intel.com>2018-03-12 18:56:51 -0800
committerFei <fei.peng@intel.com>2018-03-12 18:56:51 -0800
commitba6b214a7a23c02f4134cac2e700ea984c7c591f (patch)
treed7a821d3379e09da05166c554c45d7cfc40fbd6b
parent66d2738ea96fcce753dec1370e79a0c78f7b6adb (diff)
downloadcoreclr-ba6b214a7a23c02f4134cac2e700ea984c7c591f.tar.gz
coreclr-ba6b214a7a23c02f4134cac2e700ea984c7c591f.tar.bz2
coreclr-ba6b214a7a23c02f4134cac2e700ea984c7c591f.zip
Implement AVX Shuffle and Permute intrinsics
-rw-r--r--src/jit/hwintrinsiclistxarch.h2
-rw-r--r--src/jit/instrsxarch.h3
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_r.csproj6
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Avx_ro.csproj6
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.1.cs313
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Double.2.cs313
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.1.cs313
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Permute.Single.2.cs313
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Program.Avx.cs6
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Shuffle.Double.1.cs337
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Avx/Shuffle.Single.1.cs337
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx8
-rw-r--r--tests/src/JIT/HardwareIntrinsics/X86/Shared/ImmBinOpTest.template337
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();
+ }
+ }
+ }
+}