Adding tests for the SSE3 and SSSE3 hardware intrinsics
authorTanner Gooding <tagoo@outlook.com>
Fri, 16 Feb 2018 20:13:05 +0000 (12:13 -0800)
committerTanner Gooding <tagoo@outlook.com>
Thu, 22 Feb 2018 06:42:13 +0000 (22:42 -0800)
44 files changed:
tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest.template [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest_DataTable.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Shared/GenerateTests.csx
tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest.template [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest_DataTable.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Double.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Single.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Double.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Single.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Double.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Single.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_r.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_ro.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_r.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_ro.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_r.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_ro.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/Program.Sse3.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_ro.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_r.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_ro.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int32.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAddSaturate.Int16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int32.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtractSaturate.Int16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyAddAdjacent.Int16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyHighRoundScale.Int16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Program.Ssse3.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int16.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int32.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.SByte.cs [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_r.csproj [new file with mode: 0644]
tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_ro.csproj [new file with mode: 0644]

diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest.template b/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest.template
new file mode 100644 (file)
index 0000000..f455f4b
--- /dev/null
@@ -0,0 +1,329 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{{
+    public static partial class Program
+    {{
+        private static void {2}{4}()
+        {{
+            var test = new AlternatingBinaryOpTest__{2}{4}();
+
+            if (test.IsSupported)
+            {{
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if ({1}.IsSupported)
+                {{
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }}
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if ({1}.IsSupported)
+                {{
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }}
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if ({1}.IsSupported)
+                {{
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }}
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }}
+            else
+            {{
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }}
+
+            if (!test.Succeeded)
+            {{
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }}
+        }}
+    }}
+
+    public sealed unsafe class AlternatingBinaryOpTest__{2}{4}
+    {{
+        private const int VectorSize = {9};
+
+        private const int Op1ElementCount = VectorSize / sizeof({6});
+        private const int Op2ElementCount = VectorSize / sizeof({8});
+        private const int RetElementCount = VectorSize / sizeof({4});
+
+        private static {6}[] _data1 = new {6}[Op1ElementCount];
+        private static {8}[] _data2 = new {8}[Op2ElementCount];
+
+        private static {5}<{6}> _clsVar1;
+        private static {7}<{8}> _clsVar2;
+
+        private {5}<{6}> _fld1;
+        private {7}<{8}> _fld2;
+
+        private AlternatingBinaryOpTest__DataTable<{4}, {6}, {8}> _dataTable;
+
+        static AlternatingBinaryOpTest__{2}{4}()
+        {{
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _clsVar1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _clsVar2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize);
+        }}
+
+        public AlternatingBinaryOpTest__{2}{4}()
+        {{
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _fld1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _fld2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }}
+            for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }}
+            _dataTable = new AlternatingBinaryOpTest__DataTable<{4}, {6}, {8}>(_data1, _data2, new {4}[RetElementCount], VectorSize);
+        }}
+
+        public bool IsSupported => {0}.IsSupported;
+
+        public bool Succeeded {{ get; set; }}
+
+        public void RunBasicScenario_UnsafeRead()
+        {{
+            var result = {0}.{2}(
+                Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunBasicScenario_Load()
+        {{
+            var result = {0}.{2}(
+                {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)),
+                {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunBasicScenario_LoadAligned()
+        {{
+            var result = {0}.{2}(
+                {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)),
+                {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunReflectionScenario_UnsafeRead()
+        {{
+            var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }})
+                                     .Invoke(null, new object[] {{
+                                        Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr)
+                                     }});
+
+            Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunReflectionScenario_Load()
+        {{
+            var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }})
+                                     .Invoke(null, new object[] {{
+                                        {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)),
+                                        {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr))
+                                     }});
+
+            Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunReflectionScenario_LoadAligned()
+        {{
+            var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }})
+                                     .Invoke(null, new object[] {{
+                                        {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)),
+                                        {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr))
+                                     }});
+
+            Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunClsVarScenario()
+        {{
+            var result = {0}.{2}(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclVarScenario_UnsafeRead()
+        {{
+            var left = Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr);
+            var result = {0}.{2}(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclVarScenario_Load()
+        {{
+            var left = {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr));
+            var right = {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr));
+            var result = {0}.{2}(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclVarScenario_LoadAligned()
+        {{
+            var left = {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr));
+            var right = {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr));
+            var result = {0}.{2}(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclFldScenario()
+        {{
+            var test = new AlternatingBinaryOpTest__{2}{4}();
+            var result = {0}.{2}(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }}
+
+        public void RunFldScenario()
+        {{
+            var result = {0}.{2}(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }}
+
+        public void RunUnsupportedScenario()
+        {{
+            Succeeded = false;
+
+            try
+            {{
+                RunBasicScenario_UnsafeRead();
+            }}
+            catch (PlatformNotSupportedException)
+            {{
+                Succeeded = true;
+            }}
+        }}
+
+        private void ValidateResult({5}<{6}> left, {7}<{8}> right, void* result, [CallerMemberName] string method = "")
+        {{
+            {6}[] inArray1 = new {6}[Op1ElementCount];
+            {8}[] inArray2 = new {8}[Op2ElementCount];
+            {4}[] outArray = new {4}[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }}
+
+        private void ValidateResult(void* left, void* right, void* result, [CallerMemberName] string method = "")
+        {{
+            {6}[] inArray1 = new {6}[Op1ElementCount];
+            {8}[] inArray2 = new {8}[Op2ElementCount];
+            {4}[] outArray = new {4}[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{6}, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{8}, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }}
+
+        private void ValidateResult({6}[] left, {8}[] right, {4}[] result, [CallerMemberName] string method = "")
+        {{
+            for (var i = 0; i < RetElementCount; i += 2)
+            {{
+                if ({12})
+                {{
+                    Succeeded = false;
+                    break;
+                }}
+
+                if ({13})
+                {{
+                    Succeeded = false;
+                    break;
+                }}
+            }}
+
+            if (!Succeeded)
+            {{
+                Console.WriteLine($"{{nameof({0})}}.{{nameof({0}.{2})}}<{4}>({5}<{6}>, {7}<{8}>): {{method}} failed:");
+                Console.WriteLine($"    left: ({{string.Join(", ", left)}})");
+                Console.WriteLine($"   right: ({{string.Join(", ", right)}})");
+                Console.WriteLine($"  result: ({{string.Join(", ", result)}})");
+                Console.WriteLine();
+            }}
+        }}
+    }}
+}}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest_DataTable.cs b/tests/src/JIT/HardwareIntrinsics/X86/Shared/AlternatingBinOpTest_DataTable.cs
new file mode 100644 (file)
index 0000000..a2f9977
--- /dev/null
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public unsafe struct AlternatingBinaryOpTest__DataTable<TResult, TOp1, TOp2> : IDisposable
+        where TResult : struct
+        where TOp1 : struct
+        where TOp2 : struct
+    {
+        private byte[] inArray1;
+        private byte[] inArray2;
+        private byte[] outArray;
+
+        private GCHandle inHandle1;
+        private GCHandle inHandle2;
+        private GCHandle outHandle;
+
+        private byte simdSize;
+
+        public AlternatingBinaryOpTest__DataTable(TOp1[] inArray1, TOp2[] inArray2, TResult[] outArray, int simdSize)
+        {
+            this.inArray1 = new byte[simdSize * 2];
+            this.inArray2 = new byte[simdSize * 2];
+            this.outArray = new byte[simdSize * 2];
+
+            this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned);
+            this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned);
+            this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned);
+
+            this.simdSize = unchecked((byte)(simdSize));
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray1Ptr), ref Unsafe.As<TOp1, byte>(ref inArray1[0]), this.simdSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray2Ptr), ref Unsafe.As<TOp2, byte>(ref inArray2[0]), this.simdSize);
+        }
+
+        public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), simdSize);
+        public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), simdSize);
+        public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), simdSize);
+
+        public void Dispose()
+        {
+            inHandle1.Free();
+            inHandle2.Free();
+            outHandle.Free();
+        }
+
+        private static unsafe void* Align(byte* buffer, byte expectedAlignment)
+        {
+            // Compute how bad the misalignment is, which is at most (expectedAlignment - 1).
+            // Then subtract that from the expectedAlignment and add it to the original address
+            // to compute the aligned address.
+
+            var misalignment = expectedAlignment - ((ulong)(buffer) % expectedAlignment);
+            return (void*)(buffer + misalignment);
+        }
+    }
+}
index 2827f95..4351de6 100644 (file)
@@ -166,6 +166,37 @@ private static readonly (string templateFileName, string[] templateData)[] Sse2I
     ("SimpleBinOpTest.template", new string[] { "Sse2", "Sse2",  "Xor",                          "Vector128",   "UInt64",    "Vector128",    "UInt64",    "Vector128",   "UInt64",                                 "16",       "(ulong)(random.Next(0, int.MaxValue))",                "(ulong)(random.Next(0, int.MaxValue))",                              "(ulong)(left[0] ^ right[0]) != result[0]",                                                                                           "(ulong)(left[i] ^ right[i]) != result[i]"}),
 };
 
+private static readonly (string templateFileName, string[] templateData)[] Sse3Inputs = new []
+{
+    // TemplateName                                  Isa,    LoadIsa, Method,               RetVectorType, RetBaseType, Op1VectorType, Op1BaseType, Op2VectorType, Op2BaseType, Op3VectorType, Op3BaseType, VectorSize, NextValueOp1,                    NextValueOp2,                    NextValueOp3, ValidateFirstResult,                                                                                     ValidateRemainingResults
+    ("AlternatingBinOpTest.template", new string[] { "Sse3", "Sse2",  "AddSubtract",        "Vector128",   "Double",    "Vector128",   "Double",    "Vector128",   "Double",                                "16",       "(double)(random.NextDouble())", "(double)(random.NextDouble())",               "BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits(left[i] - right[i])",       "BitConverter.DoubleToInt64Bits(result[i + 1]) != BitConverter.DoubleToInt64Bits(left[i + 1] + right[i + 1])"}),
+    ("AlternatingBinOpTest.template", new string[] { "Sse3", "Sse" ,  "AddSubtract",        "Vector128",   "Single",    "Vector128",   "Single",    "Vector128",   "Single",                                "16",       "(float)(random.NextDouble())",  "(float)(random.NextDouble())",                "BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(left[i] - right[i])",       "BitConverter.SingleToInt32Bits(result[i + 1]) != BitConverter.SingleToInt32Bits(left[i + 1] + right[i + 1])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Sse3", "Sse2",  "HorizontalAdd",      "Vector128",   "Double",    "Vector128",   "Double",    "Vector128",   "Double",                                "16",       "(double)(random.NextDouble())", "(double)(random.NextDouble())",               "BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] + left[i3 + 1])", "BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] + right[i3 + 1])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Sse3", "Sse",   "HorizontalAdd",      "Vector128",   "Single",    "Vector128",   "Single",    "Vector128",   "Single",                                "16",       "(float)(random.NextDouble())",  "(float)(random.NextDouble())",                "BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] + left[i3 + 1])", "BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] + right[i3 + 1])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Sse3", "Sse2",  "HorizontalSubtract", "Vector128",   "Double",    "Vector128",   "Double",    "Vector128",   "Double",                                "16",       "(double)(random.NextDouble())", "(double)(random.NextDouble())",               "BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] - left[i3 + 1])", "BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] - right[i3 + 1])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Sse3", "Sse",   "HorizontalSubtract", "Vector128",   "Single",    "Vector128",   "Single",    "Vector128",   "Single",                                "16",       "(float)(random.NextDouble())",  "(float)(random.NextDouble())",                "BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] - left[i3 + 1])", "BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] - right[i3 + 1])"}),
+};
+
+private static readonly (string templateFileName, string[] templateData)[] Ssse3Inputs = new []
+{
+    // TemplateName                                  Isa,     LoadIsa, Method,                       RetVectorType, RetBaseType, Op1VectorType, Op1BaseType, Op2VectorType, Op2BaseType, Op3VectorType, Op3BaseType, VectorSize, NextValueOp1,                                               NextValueOp2,                                           NextValueOp3, ValidateFirstResult,                                                                                      ValidateRemainingResults
+    ("SimpleUnOpTest.template",       new string[] { "Ssse3", "Sse2",  "Abs",                        "Vector128",   "Byte",      "Vector128",   "SByte",                                                             "16",       "(sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue))",                                                                       "result[0] != Math.Abs(firstOp[0])",                                                                      "result[i] != Math.Abs(firstOp[i])"}),
+    ("SimpleUnOpTest.template",       new string[] { "Ssse3", "Sse2",  "Abs",                        "Vector128",   "UInt16",    "Vector128",   "Int16",                                                             "16",       "(short)(random.Next(short.MinValue + 1, short.MaxValue))",                                                                       "result[0] != Math.Abs(firstOp[0])",                                                                      "result[i] != Math.Abs(firstOp[i])"}),
+    ("SimpleUnOpTest.template",       new string[] { "Ssse3", "Sse2",  "Abs",                        "Vector128",   "UInt32",    "Vector128",   "Int32",                                                             "16",       "(int)(random.Next(int.MinValue + 1, int.MaxValue))",                                                                             "result[0] != Math.Abs(firstOp[0])",                                                                      "result[i] != Math.Abs(firstOp[i])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Ssse3", "Sse2",  "HorizontalAdd",              "Vector128",   "Int16",     "Vector128",   "Int16",     "Vector128",   "Int16",                                 "16",       "(short)(random.Next(short.MinValue, short.MaxValue))",     "(short)(random.Next(short.MinValue, short.MaxValue))",               "result[i1] != (short)(left[i3] + left[i3 + 1])",                                                         "result[i2] != (short)(right[i3] + right[i3 + 1])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Ssse3", "Sse2",  "HorizontalAdd",              "Vector128",   "Int32",     "Vector128",   "Int32",     "Vector128",   "Int32",                                 "16",       "(int)(random.Next(int.MinValue, int.MaxValue))",           "(int)(random.Next(int.MinValue, int.MaxValue))",                     "result[i1] != (int)(left[i3] + left[i3 + 1])",                                                           "result[i2] != (int)(right[i3] + right[i3 + 1])"}),    
+    ("HorizontalBinOpTest.template",  new string[] { "Ssse3", "Sse2",  "HorizontalAddSaturate",      "Vector128",   "Int16",     "Vector128",   "Int16",     "Vector128",   "Int16",                                 "16",       "(short)(random.Next(short.MinValue, short.MaxValue))",     "(short)(random.Next(short.MinValue, short.MaxValue))",               "result[i1] != Math.Clamp((left[i3] + left[i3 + 1]), short.MinValue, short.MaxValue)",                    "result[i2] != Math.Clamp((right[i3] + right[i3 + 1]), short.MinValue, short.MaxValue)"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Ssse3", "Sse2",  "HorizontalSubtract",         "Vector128",   "Int16",     "Vector128",   "Int16",     "Vector128",   "Int16",                                 "16",       "(short)(random.Next(short.MinValue, short.MaxValue))",     "(short)(random.Next(short.MinValue, short.MaxValue))",               "result[i1] != (short)(left[i3] - left[i3 + 1])",                                                         "result[i2] != (short)(right[i3] - right[i3 + 1])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Ssse3", "Sse2",  "HorizontalSubtract",         "Vector128",   "Int32",     "Vector128",   "Int32",     "Vector128",   "Int32",                                 "16",       "(int)(random.Next(int.MinValue, int.MaxValue))",           "(int)(random.Next(int.MinValue, int.MaxValue))",                     "result[i1] != (int)(left[i3] - left[i3 + 1])",                                                           "result[i2] != (int)(right[i3] - right[i3 + 1])"}),
+    ("HorizontalBinOpTest.template",  new string[] { "Ssse3", "Sse2",  "HorizontalSubtractSaturate", "Vector128",   "Int16",     "Vector128",   "Int16",     "Vector128",   "Int16",                                 "16",       "(short)(random.Next(short.MinValue, short.MaxValue))",     "(short)(random.Next(short.MinValue, short.MaxValue))",               "result[i1] != Math.Clamp((left[i3] - left[i3 + 1]), short.MinValue, short.MaxValue)",                    "result[i2] != Math.Clamp((right[i3] - right[i3 + 1]), short.MinValue, short.MaxValue)"}),
+    ("SimpleBinOpTest.template",      new string[] { "Ssse3", "Sse2",  "MultiplyAddAdjacent",        "Vector128",   "Int16",     "Vector128",   "Byte",      "Vector128",   "SByte",                                 "16",       "(byte)(random.Next(0, byte.MaxValue))",                    "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))",               "result[0] != Math.Clamp(((right[1] * left[1]) + (right[0] * left[0])), short.MinValue, short.MaxValue)", "result[i] != Math.Clamp(((right[(i * 2) + 1] * left[(i * 2) + 1]) + (right[i * 2] * left[i * 2])), short.MinValue, short.MaxValue)"}),
+    ("SimpleBinOpTest.template",      new string[] { "Ssse3", "Sse2",  "MultiplyHighRoundScale",     "Vector128",   "Int16",     "Vector128",   "Int16",     "Vector128",   "Int16",                                 "16",       "(short)(random.Next(short.MinValue, short.MaxValue))",     "(short)(random.Next(short.MinValue, short.MaxValue))",               "result[0] != (short)((((left[0] * right[0]) >> 14) + 1) >> 1)",                                          "result[i] != (short)((((left[i] * right[i]) >> 14) + 1) >> 1)"}),
+    ("SimpleBinOpTest.template",      new string[] { "Ssse3", "Sse2",  "Shuffle",                    "Vector128",   "SByte",     "Vector128",   "SByte",     "Vector128",   "SByte",                                 "16",       "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))",     "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))",               "result[0] != ((right[0] < 0) ? 0 : left[right[0] & 0x0F])",                                              "result[i] != ((right[i] < 0) ? 0 : left[right[i] & 0x0F])"}),
+    ("SimpleBinOpTest.template",      new string[] { "Ssse3", "Sse2",  "Sign",                       "Vector128",   "SByte",     "Vector128",   "SByte",     "Vector128",   "SByte",                                 "16",       "(sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue))", "(sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue))",               "result[0] != (right[0] < 0 ? (sbyte)(-left[0]) : (right[0] > 0 ? left[0] : 0))",                                              "result[i] != (right[i] < 0 ? (sbyte)(-left[i]) : (right[i] > 0 ? left[i] : 0))"}),
+    ("SimpleBinOpTest.template",      new string[] { "Ssse3", "Sse2",  "Sign",                       "Vector128",   "Int16",     "Vector128",   "Int16",     "Vector128",   "Int16",                                 "16",       "(short)(random.Next(short.MinValue + 1, short.MaxValue))", "(short)(random.Next(short.MinValue, short.MaxValue))",               "result[0] != (right[0] < 0 ? (short)(-left[0]) : (right[0] > 0 ? left[0] : 0))",                                              "result[i] != (right[i] < 0 ? (short)(-left[i]) : (right[i] > 0 ? left[i] : 0))"}),
+    ("SimpleBinOpTest.template",      new string[] { "Ssse3", "Sse2",  "Sign",                       "Vector128",   "Int32",     "Vector128",   "Int32",     "Vector128",   "Int32",                                 "16",       "(int)(random.Next(int.MinValue + 1, int.MaxValue))",       "(int)(random.Next(int.MinValue, int.MaxValue))",                     "result[0] != (right[0] < 0 ? (int)(-left[0]) : (right[0] > 0 ? left[0] : 0))",                                                "result[i] != (right[i] < 0 ? (int)(-left[i]) : (right[i] > 0 ? left[i] : 0))"}),
+};
+
 private static readonly (string templateFileName, string[] templateData)[] Sse41Inputs = new []
 {
     // TemplateName                             Isa,     LoadIsa, Method,         RetVectorType, RetBaseType, Op1VectorType, Op1BaseType, Op2VectorType, Op2BaseType, Op3VectorType, Op3BaseType,  VectorSize, NextValueOp1,                                      NextValueOp2,                                      NextValueOp3, ValidateFirstResult,                                                 ValidateRemainingResults
@@ -333,6 +364,8 @@ private static void ProcessInput(StreamWriter testListFile, (string templateFile
 
 ProcessInputs("Sse", SseInputs);
 ProcessInputs("Sse2", Sse2Inputs);
+ProcessInputs("Sse3", Sse3Inputs);
+ProcessInputs("Ssse3", Ssse3Inputs);
 ProcessInputs("Sse41", Sse41Inputs);
 ProcessInputs("Sse42", Sse42Inputs);
 ProcessInputs("Avx", AvxInputs);
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest.template b/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest.template
new file mode 100644 (file)
index 0000000..c3a45df
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{{
+    public static partial class Program
+    {{
+        private static void {2}{4}()
+        {{
+            var test = new HorizontalBinaryOpTest__{2}{4}();
+
+            if (test.IsSupported)
+            {{
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if ({1}.IsSupported)
+                {{
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }}
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if ({1}.IsSupported)
+                {{
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }}
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if ({1}.IsSupported)
+                {{
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }}
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }}
+            else
+            {{
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }}
+
+            if (!test.Succeeded)
+            {{
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }}
+        }}
+    }}
+
+    public sealed unsafe class HorizontalBinaryOpTest__{2}{4}
+    {{
+        private const int VectorSize = {9};
+
+        private const int Op1ElementCount = VectorSize / sizeof({6});
+        private const int Op2ElementCount = VectorSize / sizeof({8});
+        private const int RetElementCount = VectorSize / sizeof({4});
+
+        private static {6}[] _data1 = new {6}[Op1ElementCount];
+        private static {8}[] _data2 = new {8}[Op2ElementCount];
+
+        private static {5}<{6}> _clsVar1;
+        private static {7}<{8}> _clsVar2;
+
+        private {5}<{6}> _fld1;
+        private {7}<{8}> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<{4}, {6}, {8}> _dataTable;
+
+        static HorizontalBinaryOpTest__{2}{4}()
+        {{
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _clsVar1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _clsVar2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize);
+        }}
+
+        public HorizontalBinaryOpTest__{2}{4}()
+        {{
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{5}<{6}>, byte>(ref _fld1), ref Unsafe.As<{6}, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }}
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{7}<{8}>, byte>(ref _fld2), ref Unsafe.As<{8}, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) {{ _data1[i] = {10}; }}
+            for (var i = 0; i < Op2ElementCount; i++) {{ _data2[i] = {11}; }}
+            _dataTable = new HorizontalBinaryOpTest__DataTable<{4}, {6}, {8}>(_data1, _data2, new {4}[RetElementCount], VectorSize);
+        }}
+
+        public bool IsSupported => {0}.IsSupported;
+
+        public bool Succeeded {{ get; set; }}
+
+        public void RunBasicScenario_UnsafeRead()
+        {{
+            var result = {0}.{2}(
+                Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunBasicScenario_Load()
+        {{
+            var result = {0}.{2}(
+                {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)),
+                {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunBasicScenario_LoadAligned()
+        {{
+            var result = {0}.{2}(
+                {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)),
+                {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunReflectionScenario_UnsafeRead()
+        {{
+            var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }})
+                                     .Invoke(null, new object[] {{
+                                        Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr)
+                                     }});
+
+            Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunReflectionScenario_Load()
+        {{
+            var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }})
+                                     .Invoke(null, new object[] {{
+                                        {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr)),
+                                        {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr))
+                                     }});
+
+            Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunReflectionScenario_LoadAligned()
+        {{
+            var result = typeof({0}).GetMethod(nameof({0}.{2}), new Type[] {{ typeof({5}<{6}>), typeof({7}<{8}>) }})
+                                     .Invoke(null, new object[] {{
+                                        {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr)),
+                                        {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr))
+                                     }});
+
+            Unsafe.Write(_dataTable.outArrayPtr, ({3}<{4}>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }}
+
+        public void RunClsVarScenario()
+        {{
+            var result = {0}.{2}(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclVarScenario_UnsafeRead()
+        {{
+            var left = Unsafe.Read<{5}<{6}>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<{7}<{8}>>(_dataTable.inArray2Ptr);
+            var result = {0}.{2}(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclVarScenario_Load()
+        {{
+            var left = {1}.Load{5}(({6}*)(_dataTable.inArray1Ptr));
+            var right = {1}.Load{7}(({8}*)(_dataTable.inArray2Ptr));
+            var result = {0}.{2}(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclVarScenario_LoadAligned()
+        {{
+            var left = {1}.LoadAligned{5}(({6}*)(_dataTable.inArray1Ptr));
+            var right = {1}.LoadAligned{7}(({8}*)(_dataTable.inArray2Ptr));
+            var result = {0}.{2}(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }}
+
+        public void RunLclFldScenario()
+        {{
+            var test = new HorizontalBinaryOpTest__{2}{4}();
+            var result = {0}.{2}(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }}
+
+        public void RunFldScenario()
+        {{
+            var result = {0}.{2}(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }}
+
+        public void RunUnsupportedScenario()
+        {{
+            Succeeded = false;
+
+            try
+            {{
+                RunBasicScenario_UnsafeRead();
+            }}
+            catch (PlatformNotSupportedException)
+            {{
+                Succeeded = true;
+            }}
+        }}
+
+        private void ValidateResult({5}<{6}> left, {7}<{8}> right, void* result, [CallerMemberName] string method = "")
+        {{
+            {6}[] inArray1 = new {6}[Op1ElementCount];
+            {8}[] inArray2 = new {8}[Op2ElementCount];
+            {4}[] outArray = new {4}[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }}
+
+        private void ValidateResult(void* left, void* right, void* result, [CallerMemberName] string method = "")
+        {{
+            {6}[] inArray1 = new {6}[Op1ElementCount];
+            {8}[] inArray2 = new {8}[Op2ElementCount];
+            {4}[] outArray = new {4}[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{6}, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{8}, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<{4}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }}
+
+        private void ValidateResult({6}[] left, {8}[] right, {4}[] result, [CallerMemberName] string method = "")
+        {{
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {{
+                for (var inner = 0; inner < (8 / sizeof({6})); inner++)
+                {{
+                    var i1 = (outer * (16 / sizeof({6}))) + inner;
+                    var i2 = i1 + (8 / sizeof({6}));
+                    var i3 = (outer * (16 / sizeof({6}))) + (inner * 2);
+
+                    if ({12})
+                    {{
+                        Succeeded = false;
+                        break;
+                    }}
+
+                    if ({13})
+                    {{
+                        Succeeded = false;
+                        break;
+                    }}
+                }}
+            }}
+
+            if (!Succeeded)
+            {{
+                Console.WriteLine($"{{nameof({0})}}.{{nameof({0}.{2})}}<{4}>({5}<{6}>, {7}<{8}>): {{method}} failed:");
+                Console.WriteLine($"    left: ({{string.Join(", ", left)}})");
+                Console.WriteLine($"   right: ({{string.Join(", ", right)}})");
+                Console.WriteLine($"  result: ({{string.Join(", ", result)}})");
+                Console.WriteLine();
+            }}
+        }}
+    }}
+}}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest_DataTable.cs b/tests/src/JIT/HardwareIntrinsics/X86/Shared/HorizontalBinOpTest_DataTable.cs
new file mode 100644 (file)
index 0000000..eb6af4a
--- /dev/null
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public unsafe struct HorizontalBinaryOpTest__DataTable<TResult, TOp1, TOp2> : IDisposable
+        where TResult : struct
+        where TOp1 : struct
+        where TOp2 : struct
+    {
+        private byte[] inArray1;
+        private byte[] inArray2;
+        private byte[] outArray;
+
+        private GCHandle inHandle1;
+        private GCHandle inHandle2;
+        private GCHandle outHandle;
+
+        private byte simdSize;
+
+        public HorizontalBinaryOpTest__DataTable(TOp1[] inArray1, TOp2[] inArray2, TResult[] outArray, int simdSize)
+        {
+            this.inArray1 = new byte[simdSize * 2];
+            this.inArray2 = new byte[simdSize * 2];
+            this.outArray = new byte[simdSize * 2];
+
+            this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned);
+            this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned);
+            this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned);
+
+            this.simdSize = unchecked((byte)(simdSize));
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray1Ptr), ref Unsafe.As<TOp1, byte>(ref inArray1[0]), this.simdSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray2Ptr), ref Unsafe.As<TOp2, byte>(ref inArray2[0]), this.simdSize);
+        }
+
+        public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), simdSize);
+        public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), simdSize);
+        public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), simdSize);
+
+        public void Dispose()
+        {
+            inHandle1.Free();
+            inHandle2.Free();
+            outHandle.Free();
+        }
+
+        private static unsafe void* Align(byte* buffer, byte expectedAlignment)
+        {
+            // Compute how bad the misalignment is, which is at most (expectedAlignment - 1).
+            // Then subtract that from the expectedAlignment and add it to the original address
+            // to compute the aligned address.
+
+            var misalignment = expectedAlignment - ((ulong)(buffer) % expectedAlignment);
+            return (void*)(buffer + misalignment);
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Double.cs
new file mode 100644 (file)
index 0000000..420097c
--- /dev/null
@@ -0,0 +1,329 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void AddSubtractDouble()
+        {
+            var test = new AlternatingBinaryOpTest__AddSubtractDouble();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class AlternatingBinaryOpTest__AddSubtractDouble
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Double);
+        private const int Op2ElementCount = VectorSize / sizeof(Double);
+        private const int RetElementCount = VectorSize / sizeof(Double);
+
+        private static Double[] _data1 = new Double[Op1ElementCount];
+        private static Double[] _data2 = new Double[Op2ElementCount];
+
+        private static Vector128<Double> _clsVar1;
+        private static Vector128<Double> _clsVar2;
+
+        private Vector128<Double> _fld1;
+        private Vector128<Double> _fld2;
+
+        private AlternatingBinaryOpTest__DataTable<Double, Double, Double> _dataTable;
+
+        static AlternatingBinaryOpTest__AddSubtractDouble()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<Double>, byte>(ref _clsVar2), ref Unsafe.As<Double, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public AlternatingBinaryOpTest__AddSubtractDouble()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<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 AlternatingBinaryOpTest__DataTable<Double, Double, Double>(_data1, _data2, new Double[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse3.AddSubtract(
+                Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse3.AddSubtract(
+                Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse3.AddSubtract(
+                Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse3.AddSubtract(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr);
+            var result = Sse3.AddSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr));
+            var result = Sse3.AddSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr));
+            var result = Sse3.AddSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new AlternatingBinaryOpTest__AddSubtractDouble();
+            var result = Sse3.AddSubtract(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse3.AddSubtract(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Double> left, Vector128<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 = "")
+        {
+            for (var i = 0; i < RetElementCount; i += 2)
+            {
+                if (BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits(left[i] - right[i]))
+                {
+                    Succeeded = false;
+                    break;
+                }
+
+                if (BitConverter.DoubleToInt64Bits(result[i + 1]) != BitConverter.DoubleToInt64Bits(left[i + 1] + right[i + 1]))
+                {
+                    Succeeded = false;
+                    break;
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.AddSubtract)}<Double>(Vector128<Double>, Vector128<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/Sse3/AddSubtract.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/AddSubtract.Single.cs
new file mode 100644 (file)
index 0000000..a5a1b4e
--- /dev/null
@@ -0,0 +1,329 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void AddSubtractSingle()
+        {
+            var test = new AlternatingBinaryOpTest__AddSubtractSingle();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class AlternatingBinaryOpTest__AddSubtractSingle
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Single);
+        private const int Op2ElementCount = VectorSize / sizeof(Single);
+        private const int RetElementCount = VectorSize / sizeof(Single);
+
+        private static Single[] _data1 = new Single[Op1ElementCount];
+        private static Single[] _data2 = new Single[Op2ElementCount];
+
+        private static Vector128<Single> _clsVar1;
+        private static Vector128<Single> _clsVar2;
+
+        private Vector128<Single> _fld1;
+        private Vector128<Single> _fld2;
+
+        private AlternatingBinaryOpTest__DataTable<Single, Single, Single> _dataTable;
+
+        static AlternatingBinaryOpTest__AddSubtractSingle()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<Single>, byte>(ref _clsVar2), ref Unsafe.As<Single, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public AlternatingBinaryOpTest__AddSubtractSingle()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<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 AlternatingBinaryOpTest__DataTable<Single, Single, Single>(_data1, _data2, new Single[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse3.AddSubtract(
+                Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse3.AddSubtract(
+                Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)),
+                Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse3.AddSubtract(
+                Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)),
+                Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)),
+                                        Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.AddSubtract), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)),
+                                        Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse3.AddSubtract(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr);
+            var result = Sse3.AddSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr));
+            var right = Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr));
+            var result = Sse3.AddSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr));
+            var right = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr));
+            var result = Sse3.AddSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new AlternatingBinaryOpTest__AddSubtractSingle();
+            var result = Sse3.AddSubtract(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse3.AddSubtract(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Single> left, Vector128<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 = "")
+        {
+            for (var i = 0; i < RetElementCount; i += 2)
+            {
+                if (BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(left[i] - right[i]))
+                {
+                    Succeeded = false;
+                    break;
+                }
+
+                if (BitConverter.SingleToInt32Bits(result[i + 1]) != BitConverter.SingleToInt32Bits(left[i + 1] + right[i + 1]))
+                {
+                    Succeeded = false;
+                    break;
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.AddSubtract)}<Single>(Vector128<Single>, Vector128<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/Sse3/HorizontalAdd.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Double.cs
new file mode 100644 (file)
index 0000000..ee0c9b3
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalAddDouble()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddDouble();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalAddDouble
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Double);
+        private const int Op2ElementCount = VectorSize / sizeof(Double);
+        private const int RetElementCount = VectorSize / sizeof(Double);
+
+        private static Double[] _data1 = new Double[Op1ElementCount];
+        private static Double[] _data2 = new Double[Op2ElementCount];
+
+        private static Vector128<Double> _clsVar1;
+        private static Vector128<Double> _clsVar2;
+
+        private Vector128<Double> _fld1;
+        private Vector128<Double> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Double, Double, Double> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalAddDouble()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<Double>, byte>(ref _clsVar2), ref Unsafe.As<Double, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalAddDouble()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<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 HorizontalBinaryOpTest__DataTable<Double, Double, Double>(_data1, _data2, new Double[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse3.HorizontalAdd(
+                Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse3.HorizontalAdd(
+                Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse3.HorizontalAdd(
+                Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse3.HorizontalAdd(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr);
+            var result = Sse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddDouble();
+            var result = Sse3.HorizontalAdd(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse3.HorizontalAdd(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Double> left, Vector128<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 = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Double)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Double))) + inner;
+                    var i2 = i1 + (8 / sizeof(Double));
+                    var i3 = (outer * (16 / sizeof(Double))) + (inner * 2);
+
+                    if (BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] + left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] + right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalAdd)}<Double>(Vector128<Double>, Vector128<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/Sse3/HorizontalAdd.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalAdd.Single.cs
new file mode 100644 (file)
index 0000000..ddcd847
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalAddSingle()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddSingle();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalAddSingle
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Single);
+        private const int Op2ElementCount = VectorSize / sizeof(Single);
+        private const int RetElementCount = VectorSize / sizeof(Single);
+
+        private static Single[] _data1 = new Single[Op1ElementCount];
+        private static Single[] _data2 = new Single[Op2ElementCount];
+
+        private static Vector128<Single> _clsVar1;
+        private static Vector128<Single> _clsVar2;
+
+        private Vector128<Single> _fld1;
+        private Vector128<Single> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Single, Single, Single> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalAddSingle()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<Single>, byte>(ref _clsVar2), ref Unsafe.As<Single, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalAddSingle()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<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 HorizontalBinaryOpTest__DataTable<Single, Single, Single>(_data1, _data2, new Single[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse3.HorizontalAdd(
+                Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse3.HorizontalAdd(
+                Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)),
+                Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse3.HorizontalAdd(
+                Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)),
+                Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)),
+                                        Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)),
+                                        Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse3.HorizontalAdd(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr);
+            var result = Sse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr));
+            var right = Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr));
+            var right = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddSingle();
+            var result = Sse3.HorizontalAdd(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse3.HorizontalAdd(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Single> left, Vector128<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 = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Single)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Single))) + inner;
+                    var i2 = i1 + (8 / sizeof(Single));
+                    var i3 = (outer * (16 / sizeof(Single))) + (inner * 2);
+
+                    if (BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] + left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] + right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalAdd)}<Single>(Vector128<Single>, Vector128<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/Sse3/HorizontalSubtract.Double.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Double.cs
new file mode 100644 (file)
index 0000000..2363341
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalSubtractDouble()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractDouble();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalSubtractDouble
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Double);
+        private const int Op2ElementCount = VectorSize / sizeof(Double);
+        private const int RetElementCount = VectorSize / sizeof(Double);
+
+        private static Double[] _data1 = new Double[Op1ElementCount];
+        private static Double[] _data2 = new Double[Op2ElementCount];
+
+        private static Vector128<Double> _clsVar1;
+        private static Vector128<Double> _clsVar2;
+
+        private Vector128<Double> _fld1;
+        private Vector128<Double> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Double, Double, Double> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalSubtractDouble()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<Double>, byte>(ref _clsVar2), ref Unsafe.As<Double, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalSubtractDouble()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (double)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<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 HorizontalBinaryOpTest__DataTable<Double, Double, Double>(_data1, _data2, new Double[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse3.HorizontalSubtract(
+                Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse3.HorizontalSubtract(
+                Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse3.HorizontalSubtract(
+                Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128<Double>), typeof(Vector128<Double>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Double>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse3.HorizontalSubtract(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Double>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Double>>(_dataTable.inArray2Ptr);
+            var result = Sse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Double*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Double*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Double*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractDouble();
+            var result = Sse3.HorizontalSubtract(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse3.HorizontalSubtract(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Double> left, Vector128<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 = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Double)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Double))) + inner;
+                    var i2 = i1 + (8 / sizeof(Double));
+                    var i3 = (outer * (16 / sizeof(Double))) + (inner * 2);
+
+                    if (BitConverter.DoubleToInt64Bits(result[i1]) != BitConverter.DoubleToInt64Bits(left[i3] - left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (BitConverter.DoubleToInt64Bits(result[i2]) != BitConverter.DoubleToInt64Bits(right[i3] - right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalSubtract)}<Double>(Vector128<Double>, Vector128<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/Sse3/HorizontalSubtract.Single.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/HorizontalSubtract.Single.cs
new file mode 100644 (file)
index 0000000..1818e3f
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalSubtractSingle()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractSingle();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalSubtractSingle
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Single);
+        private const int Op2ElementCount = VectorSize / sizeof(Single);
+        private const int RetElementCount = VectorSize / sizeof(Single);
+
+        private static Single[] _data1 = new Single[Op1ElementCount];
+        private static Single[] _data2 = new Single[Op2ElementCount];
+
+        private static Vector128<Single> _clsVar1;
+        private static Vector128<Single> _clsVar2;
+
+        private Vector128<Single> _fld1;
+        private Vector128<Single> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Single, Single, Single> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalSubtractSingle()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<Single>, byte>(ref _clsVar2), ref Unsafe.As<Single, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalSubtractSingle()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (float)(random.NextDouble()); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<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<Vector128<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 HorizontalBinaryOpTest__DataTable<Single, Single, Single>(_data1, _data2, new Single[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Sse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Sse3.HorizontalSubtract(
+                Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Sse3.HorizontalSubtract(
+                Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)),
+                Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Sse3.HorizontalSubtract(
+                Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)),
+                Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr)),
+                                        Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Sse3).GetMethod(nameof(Sse3.HorizontalSubtract), new Type[] { typeof(Vector128<Single>), typeof(Vector128<Single>) })
+                                     .Invoke(null, new object[] {
+                                        Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr)),
+                                        Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Single>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Sse3.HorizontalSubtract(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Single>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Single>>(_dataTable.inArray2Ptr);
+            var result = Sse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse.LoadVector128((Single*)(_dataTable.inArray1Ptr));
+            var right = Sse.LoadVector128((Single*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray1Ptr));
+            var right = Sse.LoadAlignedVector128((Single*)(_dataTable.inArray2Ptr));
+            var result = Sse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractSingle();
+            var result = Sse3.HorizontalSubtract(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Sse3.HorizontalSubtract(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Single> left, Vector128<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 = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Single)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Single))) + inner;
+                    var i2 = i1 + (8 / sizeof(Single));
+                    var i3 = (outer * (16 / sizeof(Single))) + (inner * 2);
+
+                    if (BitConverter.SingleToInt32Bits(result[i1]) != BitConverter.SingleToInt32Bits(left[i3] - left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (BitConverter.SingleToInt32Bits(result[i2]) != BitConverter.SingleToInt32Bits(right[i3] - right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Sse3)}.{nameof(Sse3.HorizontalSubtract)}<Single>(Vector128<Single>, Vector128<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/Sse3/MoveAndDuplicate.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate.cs
new file mode 100644 (file)
index 0000000..4c843f8
--- /dev/null
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.X86;
+using System.Runtime.Intrinsics;
+
+namespace IntelHardwareIntrinsicTest
+{
+    class Program
+    {
+        const int Pass = 100;
+        const int Fail = 0;
+
+        static unsafe int Main(string[] args)
+        {
+            int testResult = Pass;
+
+            if (Sse3.IsSupported)
+            {
+                using (TestTable<double> doubleTable = new TestTable<double>(new double[2] { 1, -5 }, new double[2]))
+                {
+                    var vf1 = Unsafe.Read<Vector128<double>>(doubleTable.inArrayPtr);
+                    var vf2 = Sse3.MoveAndDuplicate(vf1);
+                    Unsafe.Write(doubleTable.outArrayPtr, vf2);
+
+                    if (BitConverter.DoubleToInt64Bits(doubleTable.inArray[0]) != BitConverter.DoubleToInt64Bits(doubleTable.outArray[0]) || 
+                        BitConverter.DoubleToInt64Bits(doubleTable.inArray[0]) != BitConverter.DoubleToInt64Bits(doubleTable.outArray[1]))
+                    {
+                        Console.WriteLine("Sse3 MoveAndDuplicate failed on double:");
+                        foreach (var item in doubleTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+                }
+            }
+
+            return testResult;
+        }
+
+        public unsafe struct TestTable<T> : IDisposable where T : struct
+        {
+            public T[] inArray;
+            public T[] outArray;
+
+            public void* inArrayPtr => inHandle.AddrOfPinnedObject().ToPointer();
+            public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer();
+
+            GCHandle inHandle;
+            GCHandle outHandle;
+            public TestTable(T[] a, T[] b)
+            {
+                this.inArray = a;
+                this.outArray = b;
+
+                inHandle = GCHandle.Alloc(inArray, GCHandleType.Pinned);
+                outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned);
+            }
+            public bool CheckResult(Func<T, T, bool> check)
+            {
+                for (int i = 0; i < inArray.Length; i++)
+                {
+                    if (!check(inArray[i], outArray[i]))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            public void Dispose()
+            {
+                inHandle.Free();
+                outHandle.Free();
+            }
+        }
+
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_r.csproj
new file mode 100644 (file)
index 0000000..56f7cbf
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize></Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MoveAndDuplicate.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveAndDuplicate_ro.csproj
new file mode 100644 (file)
index 0000000..c723233
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MoveAndDuplicate.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate.cs
new file mode 100644 (file)
index 0000000..edee8e9
--- /dev/null
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.X86;
+using System.Runtime.Intrinsics;
+
+namespace IntelHardwareIntrinsicTest
+{
+    class Program
+    {
+        const int Pass = 100;
+        const int Fail = 0;
+
+        static unsafe int Main(string[] args)
+        {
+            int testResult = Pass;
+
+            if (Sse3.IsSupported)
+            {
+                using (TestTable<float> floatTable = new TestTable<float>(new float[4] { 1, -5, 100, 0 }, new float[4]))
+                {
+                    var vf1 = Sse.LoadVector128((float*)(floatTable.inArrayPtr));
+                    var vf2 = Sse3.MoveHighAndDuplicate(vf1);
+                    Unsafe.Write(floatTable.outArrayPtr, vf2);
+
+                    if (BitConverter.SingleToInt32Bits(floatTable.inArray[1]) != BitConverter.SingleToInt32Bits(floatTable.outArray[0]) || 
+                        BitConverter.SingleToInt32Bits(floatTable.inArray[1]) != BitConverter.SingleToInt32Bits(floatTable.outArray[1]) ||
+                        BitConverter.SingleToInt32Bits(floatTable.inArray[3]) != BitConverter.SingleToInt32Bits(floatTable.outArray[2]) ||
+                        BitConverter.SingleToInt32Bits(floatTable.inArray[3]) != BitConverter.SingleToInt32Bits(floatTable.outArray[3]))
+                    {
+                        Console.WriteLine("Sse3 MoveHighAndDuplicate failed on float:");
+                        foreach (var item in floatTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+                }
+            }
+
+            return testResult;
+        }
+
+        public unsafe struct TestTable<T> : IDisposable where T : struct
+        {
+            public T[] inArray;
+            public T[] outArray;
+
+            public void* inArrayPtr => inHandle.AddrOfPinnedObject().ToPointer();
+            public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer();
+
+            GCHandle inHandle;
+            GCHandle outHandle;
+            public TestTable(T[] a, T[] b)
+            {
+                this.inArray = a;
+                this.outArray = b;
+
+                inHandle = GCHandle.Alloc(inArray, GCHandleType.Pinned);
+                outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned);
+            }
+            public bool CheckResult(Func<T, T, bool> check)
+            {
+                for (int i = 0; i < inArray.Length; i++)
+                {
+                    if (!check(inArray[i], outArray[i]))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            public void Dispose()
+            {
+                inHandle.Free();
+                outHandle.Free();
+            }
+        }
+
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_r.csproj
new file mode 100644 (file)
index 0000000..293b6a0
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize></Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MoveHighAndDuplicate.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveHighAndDuplicate_ro.csproj
new file mode 100644 (file)
index 0000000..5a367be
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MoveHighAndDuplicate.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate.cs
new file mode 100644 (file)
index 0000000..ee46de0
--- /dev/null
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.X86;
+using System.Runtime.Intrinsics;
+
+namespace IntelHardwareIntrinsicTest
+{
+    class Program
+    {
+        const int Pass = 100;
+        const int Fail = 0;
+
+        static unsafe int Main(string[] args)
+        {
+            int testResult = Pass;
+
+            if (Sse3.IsSupported)
+            {
+                using (TestTable<float> floatTable = new TestTable<float>(new float[4] { 1, -5, 100, 0 }, new float[4]))
+                {
+                    var vf1 = Sse.LoadVector128((float*)(floatTable.inArrayPtr));
+                    var vf2 = Sse3.MoveLowAndDuplicate(vf1);
+                    Unsafe.Write(floatTable.outArrayPtr, vf2);
+
+                    if (BitConverter.SingleToInt32Bits(floatTable.inArray[0]) != BitConverter.SingleToInt32Bits(floatTable.outArray[0]) || 
+                        BitConverter.SingleToInt32Bits(floatTable.inArray[0]) != BitConverter.SingleToInt32Bits(floatTable.outArray[1]) ||
+                        BitConverter.SingleToInt32Bits(floatTable.inArray[2]) != BitConverter.SingleToInt32Bits(floatTable.outArray[2]) ||
+                        BitConverter.SingleToInt32Bits(floatTable.inArray[2]) != BitConverter.SingleToInt32Bits(floatTable.outArray[3]))
+                    {
+                        Console.WriteLine("Sse3 MoveLowAndDuplicate failed on float:");
+                        foreach (var item in floatTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+                }
+            }
+
+            return testResult;
+        }
+
+        public unsafe struct TestTable<T> : IDisposable where T : struct
+        {
+            public T[] inArray;
+            public T[] outArray;
+
+            public void* inArrayPtr => inHandle.AddrOfPinnedObject().ToPointer();
+            public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer();
+
+            GCHandle inHandle;
+            GCHandle outHandle;
+            public TestTable(T[] a, T[] b)
+            {
+                this.inArray = a;
+                this.outArray = b;
+
+                inHandle = GCHandle.Alloc(inArray, GCHandleType.Pinned);
+                outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned);
+            }
+            public bool CheckResult(Func<T, T, bool> check)
+            {
+                for (int i = 0; i < inArray.Length; i++)
+                {
+                    if (!check(inArray[i], outArray[i]))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            public void Dispose()
+            {
+                inHandle.Free();
+                outHandle.Free();
+            }
+        }
+
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_r.csproj
new file mode 100644 (file)
index 0000000..50d2915
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize></Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MoveLowAndDuplicate.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/MoveLowAndDuplicate_ro.csproj
new file mode 100644 (file)
index 0000000..3dfda31
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MoveLowAndDuplicate.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Program.Sse3.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Program.Sse3.cs
new file mode 100644 (file)
index 0000000..13060f8
--- /dev/null
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        static Program()
+        {
+            TestList = new Dictionary<string, Action>() {
+                ["AddSubtract.Double"] = AddSubtractDouble,
+                ["AddSubtract.Single"] = AddSubtractSingle,
+                ["HorizontalAdd.Double"] = HorizontalAddDouble,
+                ["HorizontalAdd.Single"] = HorizontalAddSingle,
+                ["HorizontalSubtract.Double"] = HorizontalSubtractDouble,
+                ["HorizontalSubtract.Single"] = HorizontalSubtractSingle,
+            };
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_r.csproj
new file mode 100644 (file)
index 0000000..523c4f5
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize></Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="AddSubtract.Double.cs" />
+    <Compile Include="AddSubtract.Single.cs" />
+    <Compile Include="HorizontalAdd.Double.cs" />
+    <Compile Include="HorizontalAdd.Single.cs" />
+    <Compile Include="HorizontalSubtract.Double.cs" />
+    <Compile Include="HorizontalSubtract.Single.cs" />
+    <Compile Include="Program.Sse3.cs" />
+    <Compile Include="..\Shared\Program.cs" />
+    <Compile Include="..\Shared\AlternatingBinOpTest_DataTable.cs" />
+    <Compile Include="..\Shared\HorizontalBinOpTest_DataTable.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse3/Sse3_ro.csproj
new file mode 100644 (file)
index 0000000..9a4933e
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="AddSubtract.Double.cs" />
+    <Compile Include="AddSubtract.Single.cs" />
+    <Compile Include="HorizontalAdd.Double.cs" />
+    <Compile Include="HorizontalAdd.Single.cs" />
+    <Compile Include="HorizontalSubtract.Double.cs" />
+    <Compile Include="HorizontalSubtract.Single.cs" />
+    <Compile Include="Program.Sse3.cs" />
+    <Compile Include="..\Shared\Program.cs" />
+    <Compile Include="..\Shared\AlternatingBinOpTest_DataTable.cs" />
+    <Compile Include="..\Shared\HorizontalBinOpTest_DataTable.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.Byte.cs
new file mode 100644 (file)
index 0000000..68716f6
--- /dev/null
@@ -0,0 +1,306 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void AbsByte()
+        {
+            var test = new SimpleUnaryOpTest__AbsByte();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleUnaryOpTest__AbsByte
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(SByte);
+        private const int RetElementCount = VectorSize / sizeof(Byte);
+
+        private static SByte[] _data = new SByte[Op1ElementCount];
+
+        private static Vector128<SByte> _clsVar;
+
+        private Vector128<SByte> _fld;
+
+        private SimpleUnaryOpTest__DataTable<Byte, SByte> _dataTable;
+
+        static SimpleUnaryOpTest__AbsByte()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _clsVar), ref Unsafe.As<SByte, byte>(ref _data[0]), VectorSize);
+        }
+
+        public SimpleUnaryOpTest__AbsByte()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _fld), ref Unsafe.As<SByte, byte>(ref _data[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); }
+            _dataTable = new SimpleUnaryOpTest__DataTable<Byte, SByte>(_data, new Byte[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.Abs(
+                Unsafe.Read<Vector128<SByte>>(_dataTable.inArrayPtr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.Abs(
+                Sse2.LoadVector128((SByte*)(_dataTable.inArrayPtr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.Abs(
+                Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArrayPtr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<SByte>>(_dataTable.inArrayPtr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Byte>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((SByte*)(_dataTable.inArrayPtr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Byte>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArrayPtr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Byte>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.Abs(
+                _clsVar
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var firstOp = Unsafe.Read<Vector128<SByte>>(_dataTable.inArrayPtr);
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var firstOp = Sse2.LoadVector128((SByte*)(_dataTable.inArrayPtr));
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var firstOp = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArrayPtr));
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleUnaryOpTest__AbsByte();
+            var result = Ssse3.Abs(test._fld);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.Abs(_fld);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<SByte> firstOp, void* result, [CallerMemberName] string method = "")
+        {
+            SByte[] inArray = new SByte[Op1ElementCount];
+            Byte[] outArray = new Byte[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Byte, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray, outArray, method);
+        }
+
+        private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "")
+        {
+            SByte[] inArray = new SByte[Op1ElementCount];
+            Byte[] outArray = new Byte[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Byte, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray, outArray, method);
+        }
+
+        private void ValidateResult(SByte[] firstOp, Byte[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != Math.Abs(firstOp[0]))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != Math.Abs(firstOp[i]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Abs)}<Byte>(Vector128<SByte>): {method} failed:");
+                Console.WriteLine($"  firstOp: ({string.Join(", ", firstOp)})");
+                Console.WriteLine($"   result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt16.cs
new file mode 100644 (file)
index 0000000..8aad8e8
--- /dev/null
@@ -0,0 +1,306 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void AbsUInt16()
+        {
+            var test = new SimpleUnaryOpTest__AbsUInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleUnaryOpTest__AbsUInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int16);
+        private const int RetElementCount = VectorSize / sizeof(UInt16);
+
+        private static Int16[] _data = new Int16[Op1ElementCount];
+
+        private static Vector128<Int16> _clsVar;
+
+        private Vector128<Int16> _fld;
+
+        private SimpleUnaryOpTest__DataTable<UInt16, Int16> _dataTable;
+
+        static SimpleUnaryOpTest__AbsUInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar), ref Unsafe.As<Int16, byte>(ref _data[0]), VectorSize);
+        }
+
+        public SimpleUnaryOpTest__AbsUInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld), ref Unsafe.As<Int16, byte>(ref _data[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); }
+            _dataTable = new SimpleUnaryOpTest__DataTable<UInt16, Int16>(_data, new UInt16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.Abs(
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArrayPtr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.Abs(
+                Sse2.LoadVector128((Int16*)(_dataTable.inArrayPtr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.Abs(
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArrayPtr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArrayPtr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt16>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArrayPtr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt16>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArrayPtr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt16>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.Abs(
+                _clsVar
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var firstOp = Unsafe.Read<Vector128<Int16>>(_dataTable.inArrayPtr);
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var firstOp = Sse2.LoadVector128((Int16*)(_dataTable.inArrayPtr));
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var firstOp = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArrayPtr));
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleUnaryOpTest__AbsUInt16();
+            var result = Ssse3.Abs(test._fld);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.Abs(_fld);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int16> firstOp, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray = new Int16[Op1ElementCount];
+            UInt16[] outArray = new UInt16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray, outArray, method);
+        }
+
+        private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray = new Int16[Op1ElementCount];
+            UInt16[] outArray = new UInt16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray, outArray, method);
+        }
+
+        private void ValidateResult(Int16[] firstOp, UInt16[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != Math.Abs(firstOp[0]))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != Math.Abs(firstOp[i]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Abs)}<UInt16>(Vector128<Int16>): {method} failed:");
+                Console.WriteLine($"  firstOp: ({string.Join(", ", firstOp)})");
+                Console.WriteLine($"   result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Abs.UInt32.cs
new file mode 100644 (file)
index 0000000..7807e01
--- /dev/null
@@ -0,0 +1,306 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void AbsUInt32()
+        {
+            var test = new SimpleUnaryOpTest__AbsUInt32();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleUnaryOpTest__AbsUInt32
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int32);
+        private const int RetElementCount = VectorSize / sizeof(UInt32);
+
+        private static Int32[] _data = new Int32[Op1ElementCount];
+
+        private static Vector128<Int32> _clsVar;
+
+        private Vector128<Int32> _fld;
+
+        private SimpleUnaryOpTest__DataTable<UInt32, Int32> _dataTable;
+
+        static SimpleUnaryOpTest__AbsUInt32()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _clsVar), ref Unsafe.As<Int32, byte>(ref _data[0]), VectorSize);
+        }
+
+        public SimpleUnaryOpTest__AbsUInt32()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _fld), ref Unsafe.As<Int32, byte>(ref _data[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); }
+            _dataTable = new SimpleUnaryOpTest__DataTable<UInt32, Int32>(_data, new UInt32[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.Abs(
+                Unsafe.Read<Vector128<Int32>>(_dataTable.inArrayPtr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.Abs(
+                Sse2.LoadVector128((Int32*)(_dataTable.inArrayPtr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.Abs(
+                Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArrayPtr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int32>>(_dataTable.inArrayPtr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt32>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int32*)(_dataTable.inArrayPtr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt32>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Abs), new Type[] { typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArrayPtr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<UInt32>)(result));
+            ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.Abs(
+                _clsVar
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var firstOp = Unsafe.Read<Vector128<Int32>>(_dataTable.inArrayPtr);
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var firstOp = Sse2.LoadVector128((Int32*)(_dataTable.inArrayPtr));
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var firstOp = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArrayPtr));
+            var result = Ssse3.Abs(firstOp);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(firstOp, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleUnaryOpTest__AbsUInt32();
+            var result = Ssse3.Abs(test._fld);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.Abs(_fld);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int32> firstOp, void* result, [CallerMemberName] string method = "")
+        {
+            Int32[] inArray = new Int32[Op1ElementCount];
+            UInt32[] outArray = new UInt32[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray[0]), firstOp);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt32, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray, outArray, method);
+        }
+
+        private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "")
+        {
+            Int32[] inArray = new Int32[Op1ElementCount];
+            UInt32[] outArray = new UInt32[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<UInt32, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray, outArray, method);
+        }
+
+        private void ValidateResult(Int32[] firstOp, UInt32[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != Math.Abs(firstOp[0]))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != Math.Abs(firstOp[i]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Abs)}<UInt32>(Vector128<Int32>): {method} failed:");
+                Console.WriteLine($"  firstOp: ({string.Join(", ", firstOp)})");
+                Console.WriteLine($"   result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight.cs
new file mode 100644 (file)
index 0000000..ef075eb
--- /dev/null
@@ -0,0 +1,156 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.X86;
+using System.Runtime.Intrinsics;
+
+namespace IntelHardwareIntrinsicTest
+{
+    class Program
+    {
+        const int Pass = 100;
+        const int Fail = 0;
+
+        static unsafe int Main(string[] args)
+        {
+            int testResult = Pass;
+
+            if (Ssse3.IsSupported)
+            {
+                using (TestTable<sbyte> sbyteTable = new TestTable<sbyte>(new sbyte[16] { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, new sbyte[16] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, new sbyte[16]))
+                {
+                    var vf1 = Unsafe.Read<Vector128<sbyte>>(sbyteTable.inArray1Ptr);
+                    var vf2 = Unsafe.Read<Vector128<sbyte>>(sbyteTable.inArray2Ptr);
+
+                    var vf3 = Ssse3.AlignRight(vf1, vf2, 27);
+                    Unsafe.Write(sbyteTable.outArrayPtr, vf3);
+
+                    if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 27) && (z[01] == 28) && (z[02] == 29) && (z[03] == 30) && 
+                                                             (z[04] == 31) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) &&
+                                                             (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && 
+                                                             (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00)))
+                    {
+                        Console.WriteLine("SSE AlignRight failed on sbyte:");
+                        foreach (var item in sbyteTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+
+                    vf3 = Ssse3.AlignRight(vf1, vf2, 5);
+                    Unsafe.Write(sbyteTable.outArrayPtr, vf3);
+
+                    if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 05) && (z[01] == 06) && (z[02] == 07) && (z[03] == 08) && 
+                                                             (z[04] == 09) && (z[05] == 10) && (z[06] == 11) && (z[07] == 12) &&
+                                                             (z[08] == 13) && (z[09] == 14) && (z[10] == 15) && (z[11] == 16) && 
+                                                             (z[12] == 17) && (z[13] == 18) && (z[14] == 19) && (z[15] == 20)))
+                    {
+                        Console.WriteLine("SSE AlignRight failed on sbyte:");
+                        foreach (var item in sbyteTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+
+                    vf3 = Ssse3.AlignRight(vf1, vf2, 250);
+                    Unsafe.Write(sbyteTable.outArrayPtr, vf3);
+                    
+                    if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 00) && (z[01] == 00) && (z[02] == 00) && (z[03] == 00) && 
+                                                             (z[04] == 00) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) &&
+                                                             (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && 
+                                                             (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00)))
+                    {
+                        Console.WriteLine("SSE AlignRight failed on sbyte:");
+                        foreach (var item in sbyteTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+
+                    vf3 = Ssse3.AlignRight(vf1, vf2, 228);
+                    Unsafe.Write(sbyteTable.outArrayPtr, vf3);
+
+                    if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 00) && (z[01] == 00) && (z[02] == 00) && (z[03] == 00) && 
+                                                             (z[04] == 00) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) &&
+                                                             (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && 
+                                                             (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00)))
+                    {
+                        Console.WriteLine("SSE AlignRight failed on sbyte:");
+                        foreach (var item in sbyteTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+
+                    vf3 = (Vector128<sbyte>)typeof(Ssse3).GetMethod(nameof(Ssse3.AlignRight), new Type[] { vf1.GetType(), vf2.GetType(), typeof(byte) }).Invoke(null, new object[] { vf1, vf2, (byte)(27) });
+                    Unsafe.Write(sbyteTable.outArrayPtr, vf3);
+
+                    if (!sbyteTable.CheckResult((x, y, z) => (z[00] == 27) && (z[01] == 28) && (z[02] == 29) && (z[03] == 30) && 
+                                                             (z[04] == 31) && (z[05] == 00) && (z[06] == 00) && (z[07] == 00) &&
+                                                             (z[08] == 00) && (z[09] == 00) && (z[10] == 00) && (z[11] == 00) && 
+                                                             (z[12] == 00) && (z[13] == 00) && (z[14] == 00) && (z[15] == 00)))
+                    {
+                        Console.WriteLine("SSE AlignRight failed on sbyte:");
+                        foreach (var item in sbyteTable.outArray)
+                        {
+                            Console.Write(item + ", ");
+                        }
+                        Console.WriteLine();
+                        testResult = Fail;
+                    }
+                }
+            }
+
+            return testResult;
+        }
+
+        public unsafe struct TestTable<T> : IDisposable where T : struct
+        {
+            public T[] inArray1;
+            public T[] inArray2;
+            public T[] outArray;
+
+            public void* inArray1Ptr => inHandle1.AddrOfPinnedObject().ToPointer();
+            public void* inArray2Ptr => inHandle2.AddrOfPinnedObject().ToPointer();
+            public void* outArrayPtr => outHandle.AddrOfPinnedObject().ToPointer();
+
+            GCHandle inHandle1;
+            GCHandle inHandle2;
+            GCHandle outHandle;
+            public TestTable(T[] a, T[] b, T[] c)
+            {
+                this.inArray1 = a;
+                this.inArray2 = b;
+                this.outArray = c;
+
+                inHandle1 = GCHandle.Alloc(inArray1, GCHandleType.Pinned);
+                inHandle2 = GCHandle.Alloc(inArray2, GCHandleType.Pinned);
+                outHandle = GCHandle.Alloc(outArray, GCHandleType.Pinned);
+            }
+            public bool CheckResult(Func<T[], T[], T[], bool> check)
+            {
+                return check(inArray1, inArray2, outArray);
+            }
+
+            public void Dispose()
+            {
+                inHandle1.Free();
+                inHandle2.Free();
+                outHandle.Free();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_r.csproj
new file mode 100644 (file)
index 0000000..7cebb15
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize></Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="AlignRight.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/AlignRight_ro.csproj
new file mode 100644 (file)
index 0000000..4472e49
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="AlignRight.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int16.cs
new file mode 100644 (file)
index 0000000..8cbc5b9
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalAddInt16()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalAddInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int16);
+        private const int Op2ElementCount = VectorSize / sizeof(Int16);
+        private const int RetElementCount = VectorSize / sizeof(Int16);
+
+        private static Int16[] _data1 = new Int16[Op1ElementCount];
+        private static Int16[] _data2 = new Int16[Op2ElementCount];
+
+        private static Vector128<Int16> _clsVar1;
+        private static Vector128<Int16> _clsVar2;
+
+        private Vector128<Int16> _fld1;
+        private Vector128<Int16> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalAddInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalAddInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            _dataTable = new HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16>(_data1, _data2, new Int16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.HorizontalAdd(
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.HorizontalAdd(
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.HorizontalAdd(
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.HorizontalAdd(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddInt16();
+            var result = Ssse3.HorizontalAdd(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.HorizontalAdd(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int16> left, Vector128<Int16> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, 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 = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Int16)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Int16))) + inner;
+                    var i2 = i1 + (8 / sizeof(Int16));
+                    var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2);
+
+                    if (result[i1] != (short)(left[i3] + left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (result[i2] != (short)(right[i3] + right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalAdd)}<Int16>(Vector128<Int16>, Vector128<Int16>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAdd.Int32.cs
new file mode 100644 (file)
index 0000000..dbbbea9
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalAddInt32()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddInt32();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalAddInt32
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int32);
+        private const int Op2ElementCount = VectorSize / sizeof(Int32);
+        private const int RetElementCount = VectorSize / sizeof(Int32);
+
+        private static Int32[] _data1 = new Int32[Op1ElementCount];
+        private static Int32[] _data2 = new Int32[Op2ElementCount];
+
+        private static Vector128<Int32> _clsVar1;
+        private static Vector128<Int32> _clsVar2;
+
+        private Vector128<Int32> _fld1;
+        private Vector128<Int32> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Int32, Int32, Int32> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalAddInt32()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _clsVar1), ref Unsafe.As<Int32, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _clsVar2), ref Unsafe.As<Int32, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalAddInt32()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _fld1), ref Unsafe.As<Int32, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _fld2), ref Unsafe.As<Int32, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            _dataTable = new HorizontalBinaryOpTest__DataTable<Int32, Int32, Int32>(_data1, _data2, new Int32[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.HorizontalAdd(
+                Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.HorizontalAdd(
+                Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.HorizontalAdd(
+                Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAdd), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.HorizontalAdd(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalAdd(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddInt32();
+            var result = Ssse3.HorizontalAdd(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.HorizontalAdd(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int32> left, Vector128<Int32> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int32[] inArray1 = new Int32[Op1ElementCount];
+            Int32[] inArray2 = new Int32[Op2ElementCount];
+            Int32[] outArray = new Int32[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, 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 = "")
+        {
+            Int32[] inArray1 = new Int32[Op1ElementCount];
+            Int32[] inArray2 = new Int32[Op2ElementCount];
+            Int32[] outArray = new Int32[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int32[] left, Int32[] right, Int32[] result, [CallerMemberName] string method = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Int32)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Int32))) + inner;
+                    var i2 = i1 + (8 / sizeof(Int32));
+                    var i3 = (outer * (16 / sizeof(Int32))) + (inner * 2);
+
+                    if (result[i1] != (int)(left[i3] + left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (result[i2] != (int)(right[i3] + right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalAdd)}<Int32>(Vector128<Int32>, Vector128<Int32>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAddSaturate.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalAddSaturate.Int16.cs
new file mode 100644 (file)
index 0000000..4df9a9c
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalAddSaturateInt16()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddSaturateInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalAddSaturateInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int16);
+        private const int Op2ElementCount = VectorSize / sizeof(Int16);
+        private const int RetElementCount = VectorSize / sizeof(Int16);
+
+        private static Int16[] _data1 = new Int16[Op1ElementCount];
+        private static Int16[] _data2 = new Int16[Op2ElementCount];
+
+        private static Vector128<Int16> _clsVar1;
+        private static Vector128<Int16> _clsVar2;
+
+        private Vector128<Int16> _fld1;
+        private Vector128<Int16> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalAddSaturateInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalAddSaturateInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            _dataTable = new HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16>(_data1, _data2, new Int16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.HorizontalAddSaturate(
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.HorizontalAddSaturate(
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.HorizontalAddSaturate(
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAddSaturate), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAddSaturate), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalAddSaturate), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.HorizontalAddSaturate(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.HorizontalAddSaturate(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalAddSaturate(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalAddSaturate(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalAddSaturateInt16();
+            var result = Ssse3.HorizontalAddSaturate(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.HorizontalAddSaturate(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int16> left, Vector128<Int16> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, 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 = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Int16)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Int16))) + inner;
+                    var i2 = i1 + (8 / sizeof(Int16));
+                    var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2);
+
+                    if (result[i1] != Math.Clamp((left[i3] + left[i3 + 1]), short.MinValue, short.MaxValue))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (result[i2] != Math.Clamp((right[i3] + right[i3 + 1]), short.MinValue, short.MaxValue))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalAddSaturate)}<Int16>(Vector128<Int16>, Vector128<Int16>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int16.cs
new file mode 100644 (file)
index 0000000..457f0ed
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalSubtractInt16()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalSubtractInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int16);
+        private const int Op2ElementCount = VectorSize / sizeof(Int16);
+        private const int RetElementCount = VectorSize / sizeof(Int16);
+
+        private static Int16[] _data1 = new Int16[Op1ElementCount];
+        private static Int16[] _data2 = new Int16[Op2ElementCount];
+
+        private static Vector128<Int16> _clsVar1;
+        private static Vector128<Int16> _clsVar2;
+
+        private Vector128<Int16> _fld1;
+        private Vector128<Int16> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalSubtractInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalSubtractInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            _dataTable = new HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16>(_data1, _data2, new Int16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractInt16();
+            var result = Ssse3.HorizontalSubtract(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.HorizontalSubtract(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int16> left, Vector128<Int16> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, 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 = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Int16)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Int16))) + inner;
+                    var i2 = i1 + (8 / sizeof(Int16));
+                    var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2);
+
+                    if (result[i1] != (short)(left[i3] - left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (result[i2] != (short)(right[i3] - right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalSubtract)}<Int16>(Vector128<Int16>, Vector128<Int16>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtract.Int32.cs
new file mode 100644 (file)
index 0000000..eb20803
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalSubtractInt32()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractInt32();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalSubtractInt32
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int32);
+        private const int Op2ElementCount = VectorSize / sizeof(Int32);
+        private const int RetElementCount = VectorSize / sizeof(Int32);
+
+        private static Int32[] _data1 = new Int32[Op1ElementCount];
+        private static Int32[] _data2 = new Int32[Op2ElementCount];
+
+        private static Vector128<Int32> _clsVar1;
+        private static Vector128<Int32> _clsVar2;
+
+        private Vector128<Int32> _fld1;
+        private Vector128<Int32> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Int32, Int32, Int32> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalSubtractInt32()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _clsVar1), ref Unsafe.As<Int32, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _clsVar2), ref Unsafe.As<Int32, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalSubtractInt32()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _fld1), ref Unsafe.As<Int32, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _fld2), ref Unsafe.As<Int32, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            _dataTable = new HorizontalBinaryOpTest__DataTable<Int32, Int32, Int32>(_data1, _data2, new Int32[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtract), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.HorizontalSubtract(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalSubtract(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractInt32();
+            var result = Ssse3.HorizontalSubtract(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.HorizontalSubtract(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int32> left, Vector128<Int32> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int32[] inArray1 = new Int32[Op1ElementCount];
+            Int32[] inArray2 = new Int32[Op2ElementCount];
+            Int32[] outArray = new Int32[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, 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 = "")
+        {
+            Int32[] inArray1 = new Int32[Op1ElementCount];
+            Int32[] inArray2 = new Int32[Op2ElementCount];
+            Int32[] outArray = new Int32[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int32[] left, Int32[] right, Int32[] result, [CallerMemberName] string method = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Int32)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Int32))) + inner;
+                    var i2 = i1 + (8 / sizeof(Int32));
+                    var i3 = (outer * (16 / sizeof(Int32))) + (inner * 2);
+
+                    if (result[i1] != (int)(left[i3] - left[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (result[i2] != (int)(right[i3] - right[i3 + 1]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalSubtract)}<Int32>(Vector128<Int32>, Vector128<Int32>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtractSaturate.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/HorizontalSubtractSaturate.Int16.cs
new file mode 100644 (file)
index 0000000..369adf3
--- /dev/null
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void HorizontalSubtractSaturateInt16()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int16);
+        private const int Op2ElementCount = VectorSize / sizeof(Int16);
+        private const int RetElementCount = VectorSize / sizeof(Int16);
+
+        private static Int16[] _data1 = new Int16[Op1ElementCount];
+        private static Int16[] _data2 = new Int16[Op2ElementCount];
+
+        private static Vector128<Int16> _clsVar1;
+        private static Vector128<Int16> _clsVar2;
+
+        private Vector128<Int16> _fld1;
+        private Vector128<Int16> _fld2;
+
+        private HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16> _dataTable;
+
+        static HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            _dataTable = new HorizontalBinaryOpTest__DataTable<Int16, Int16, Int16>(_data1, _data2, new Int16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.HorizontalSubtractSaturate(
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.HorizontalSubtractSaturate(
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.HorizontalSubtractSaturate(
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtractSaturate), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtractSaturate), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.HorizontalSubtractSaturate), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.HorizontalSubtractSaturate(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.HorizontalSubtractSaturate(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalSubtractSaturate(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.HorizontalSubtractSaturate(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new HorizontalBinaryOpTest__HorizontalSubtractSaturateInt16();
+            var result = Ssse3.HorizontalSubtractSaturate(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.HorizontalSubtractSaturate(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int16> left, Vector128<Int16> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, 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 = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "")
+        {
+            for (var outer = 0; outer < (VectorSize / 16); outer++)
+            {
+                for (var inner = 0; inner < (8 / sizeof(Int16)); inner++)
+                {
+                    var i1 = (outer * (16 / sizeof(Int16))) + inner;
+                    var i2 = i1 + (8 / sizeof(Int16));
+                    var i3 = (outer * (16 / sizeof(Int16))) + (inner * 2);
+
+                    if (result[i1] != Math.Clamp((left[i3] - left[i3 + 1]), short.MinValue, short.MaxValue))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+
+                    if (result[i2] != Math.Clamp((right[i3] - right[i3 + 1]), short.MinValue, short.MaxValue))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.HorizontalSubtractSaturate)}<Int16>(Vector128<Int16>, Vector128<Int16>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyAddAdjacent.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyAddAdjacent.Int16.cs
new file mode 100644 (file)
index 0000000..113a594
--- /dev/null
@@ -0,0 +1,330 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void MultiplyAddAdjacentInt16()
+        {
+            var test = new SimpleBinaryOpTest__MultiplyAddAdjacentInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__MultiplyAddAdjacentInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Byte);
+        private const int Op2ElementCount = VectorSize / sizeof(SByte);
+        private const int RetElementCount = VectorSize / sizeof(Int16);
+
+        private static Byte[] _data1 = new Byte[Op1ElementCount];
+        private static SByte[] _data2 = new SByte[Op2ElementCount];
+
+        private static Vector128<Byte> _clsVar1;
+        private static Vector128<SByte> _clsVar2;
+
+        private Vector128<Byte> _fld1;
+        private Vector128<SByte> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<Int16, Byte, SByte> _dataTable;
+
+        static SimpleBinaryOpTest__MultiplyAddAdjacentInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Byte>, byte>(ref _clsVar1), ref Unsafe.As<Byte, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _clsVar2), ref Unsafe.As<SByte, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__MultiplyAddAdjacentInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Byte>, byte>(ref _fld1), ref Unsafe.As<Byte, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _fld2), ref Unsafe.As<SByte, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (byte)(random.Next(0, byte.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<Int16, Byte, SByte>(_data1, _data2, new Int16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.MultiplyAddAdjacent(
+                Unsafe.Read<Vector128<Byte>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.MultiplyAddAdjacent(
+                Sse2.LoadVector128((Byte*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.MultiplyAddAdjacent(
+                Sse2.LoadAlignedVector128((Byte*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyAddAdjacent), new Type[] { typeof(Vector128<Byte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Byte>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyAddAdjacent), new Type[] { typeof(Vector128<Byte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Byte*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyAddAdjacent), new Type[] { typeof(Vector128<Byte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Byte*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.MultiplyAddAdjacent(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Byte>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.MultiplyAddAdjacent(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Byte*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.MultiplyAddAdjacent(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Byte*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.MultiplyAddAdjacent(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__MultiplyAddAdjacentInt16();
+            var result = Ssse3.MultiplyAddAdjacent(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.MultiplyAddAdjacent(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Byte> left, Vector128<SByte> right, void* result, [CallerMemberName] string method = "")
+        {
+            Byte[] inArray1 = new Byte[Op1ElementCount];
+            SByte[] inArray2 = new SByte[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, 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 = "")
+        {
+            Byte[] inArray1 = new Byte[Op1ElementCount];
+            SByte[] inArray2 = new SByte[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Byte, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Byte[] left, SByte[] right, Int16[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != Math.Clamp(((right[1] * left[1]) + (right[0] * left[0])), short.MinValue, short.MaxValue))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != Math.Clamp(((right[(i * 2) + 1] * left[(i * 2) + 1]) + (right[i * 2] * left[i * 2])), short.MinValue, short.MaxValue))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.MultiplyAddAdjacent)}<Int16>(Vector128<Byte>, Vector128<SByte>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyHighRoundScale.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/MultiplyHighRoundScale.Int16.cs
new file mode 100644 (file)
index 0000000..1c71d91
--- /dev/null
@@ -0,0 +1,330 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void MultiplyHighRoundScaleInt16()
+        {
+            var test = new SimpleBinaryOpTest__MultiplyHighRoundScaleInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__MultiplyHighRoundScaleInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int16);
+        private const int Op2ElementCount = VectorSize / sizeof(Int16);
+        private const int RetElementCount = VectorSize / sizeof(Int16);
+
+        private static Int16[] _data1 = new Int16[Op1ElementCount];
+        private static Int16[] _data2 = new Int16[Op2ElementCount];
+
+        private static Vector128<Int16> _clsVar1;
+        private static Vector128<Int16> _clsVar2;
+
+        private Vector128<Int16> _fld1;
+        private Vector128<Int16> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<Int16, Int16, Int16> _dataTable;
+
+        static SimpleBinaryOpTest__MultiplyHighRoundScaleInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__MultiplyHighRoundScaleInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<Int16, Int16, Int16>(_data1, _data2, new Int16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.MultiplyHighRoundScale(
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.MultiplyHighRoundScale(
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.MultiplyHighRoundScale(
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyHighRoundScale), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyHighRoundScale), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.MultiplyHighRoundScale), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.MultiplyHighRoundScale(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.MultiplyHighRoundScale(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.MultiplyHighRoundScale(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.MultiplyHighRoundScale(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__MultiplyHighRoundScaleInt16();
+            var result = Ssse3.MultiplyHighRoundScale(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.MultiplyHighRoundScale(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int16> left, Vector128<Int16> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, 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 = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != (short)((((left[0] * right[0]) >> 14) + 1) >> 1))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != (short)((((left[i] * right[i]) >> 14) + 1) >> 1))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.MultiplyHighRoundScale)}<Int16>(Vector128<Int16>, Vector128<Int16>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Program.Ssse3.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Program.Ssse3.cs
new file mode 100644 (file)
index 0000000..bb22c09
--- /dev/null
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        static Program()
+        {
+            TestList = new Dictionary<string, Action>() {
+                ["Abs.Byte"] = AbsByte,
+                ["Abs.UInt16"] = AbsUInt16,
+                ["Abs.UInt32"] = AbsUInt32,
+                ["HorizontalAdd.Int16"] = HorizontalAddInt16,
+                ["HorizontalAdd.Int32"] = HorizontalAddInt32,
+                ["HorizontalAddSaturate.Int16"] = HorizontalAddSaturateInt16,
+                ["HorizontalSubtract.Int16"] = HorizontalSubtractInt16,
+                ["HorizontalSubtract.Int32"] = HorizontalSubtractInt32,
+                ["HorizontalSubtractSaturate.Int16"] = HorizontalSubtractSaturateInt16,
+                ["MultiplyAddAdjacent.Int16"] = MultiplyAddAdjacentInt16,
+                ["MultiplyHighRoundScale.Int16"] = MultiplyHighRoundScaleInt16,
+                ["Shuffle.SByte"] = ShuffleSByte,
+                ["Sign.SByte"] = SignSByte,
+                ["Sign.Int16"] = SignInt16,
+                ["Sign.Int32"] = SignInt32,
+            };
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Shuffle.SByte.cs
new file mode 100644 (file)
index 0000000..877c5bb
--- /dev/null
@@ -0,0 +1,330 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void ShuffleSByte()
+        {
+            var test = new SimpleBinaryOpTest__ShuffleSByte();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__ShuffleSByte
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(SByte);
+        private const int Op2ElementCount = VectorSize / sizeof(SByte);
+        private const int RetElementCount = VectorSize / sizeof(SByte);
+
+        private static SByte[] _data1 = new SByte[Op1ElementCount];
+        private static SByte[] _data2 = new SByte[Op2ElementCount];
+
+        private static Vector128<SByte> _clsVar1;
+        private static Vector128<SByte> _clsVar2;
+
+        private Vector128<SByte> _fld1;
+        private Vector128<SByte> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<SByte, SByte, SByte> _dataTable;
+
+        static SimpleBinaryOpTest__ShuffleSByte()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _clsVar1), ref Unsafe.As<SByte, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _clsVar2), ref Unsafe.As<SByte, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__ShuffleSByte()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _fld1), ref Unsafe.As<SByte, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _fld2), ref Unsafe.As<SByte, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<SByte, SByte, SByte>(_data1, _data2, new SByte[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.Shuffle(
+                Unsafe.Read<Vector128<SByte>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.Shuffle(
+                Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.Shuffle(
+                Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), new Type[] { typeof(Vector128<SByte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<SByte>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<SByte>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), new Type[] { typeof(Vector128<SByte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<SByte>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), new Type[] { typeof(Vector128<SByte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<SByte>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.Shuffle(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<SByte>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.Shuffle(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Shuffle(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Shuffle(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__ShuffleSByte();
+            var result = Ssse3.Shuffle(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.Shuffle(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<SByte> left, Vector128<SByte> right, void* result, [CallerMemberName] string method = "")
+        {
+            SByte[] inArray1 = new SByte[Op1ElementCount];
+            SByte[] inArray2 = new SByte[Op2ElementCount];
+            SByte[] outArray = new SByte[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, 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 = "")
+        {
+            SByte[] inArray1 = new SByte[Op1ElementCount];
+            SByte[] inArray2 = new SByte[Op2ElementCount];
+            SByte[] outArray = new SByte[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(SByte[] left, SByte[] right, SByte[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != ((right[0] < 0) ? 0 : left[right[0] & 0x0F]))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != ((right[i] < 0) ? 0 : left[right[i] & 0x0F]))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Shuffle)}<SByte>(Vector128<SByte>, Vector128<SByte>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int16.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int16.cs
new file mode 100644 (file)
index 0000000..2193a3b
--- /dev/null
@@ -0,0 +1,330 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void SignInt16()
+        {
+            var test = new SimpleBinaryOpTest__SignInt16();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__SignInt16
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int16);
+        private const int Op2ElementCount = VectorSize / sizeof(Int16);
+        private const int RetElementCount = VectorSize / sizeof(Int16);
+
+        private static Int16[] _data1 = new Int16[Op1ElementCount];
+        private static Int16[] _data2 = new Int16[Op2ElementCount];
+
+        private static Vector128<Int16> _clsVar1;
+        private static Vector128<Int16> _clsVar2;
+
+        private Vector128<Int16> _fld1;
+        private Vector128<Int16> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<Int16, Int16, Int16> _dataTable;
+
+        static SimpleBinaryOpTest__SignInt16()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _clsVar2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__SignInt16()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld1), ref Unsafe.As<Int16, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int16>, byte>(ref _fld2), ref Unsafe.As<Int16, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (short)(random.Next(short.MinValue + 1, short.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (short)(random.Next(short.MinValue, short.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<Int16, Int16, Int16>(_data1, _data2, new Int16[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.Sign(
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.Sign(
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.Sign(
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<Int16>), typeof(Vector128<Int16>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int16>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.Sign(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int16>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int16*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__SignInt16();
+            var result = Ssse3.Sign(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.Sign(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int16> left, Vector128<Int16> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, 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 = "")
+        {
+            Int16[] inArray1 = new Int16[Op1ElementCount];
+            Int16[] inArray2 = new Int16[Op2ElementCount];
+            Int16[] outArray = new Int16[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int16, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int16[] left, Int16[] right, Int16[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != (right[0] < 0 ? (short)(-left[0]) : (right[0] > 0 ? left[0] : 0)))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != (right[i] < 0 ? (short)(-left[i]) : (right[i] > 0 ? left[i] : 0)))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Sign)}<Int16>(Vector128<Int16>, Vector128<Int16>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int32.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.Int32.cs
new file mode 100644 (file)
index 0000000..ab27528
--- /dev/null
@@ -0,0 +1,330 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void SignInt32()
+        {
+            var test = new SimpleBinaryOpTest__SignInt32();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__SignInt32
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(Int32);
+        private const int Op2ElementCount = VectorSize / sizeof(Int32);
+        private const int RetElementCount = VectorSize / sizeof(Int32);
+
+        private static Int32[] _data1 = new Int32[Op1ElementCount];
+        private static Int32[] _data2 = new Int32[Op2ElementCount];
+
+        private static Vector128<Int32> _clsVar1;
+        private static Vector128<Int32> _clsVar2;
+
+        private Vector128<Int32> _fld1;
+        private Vector128<Int32> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<Int32, Int32, Int32> _dataTable;
+
+        static SimpleBinaryOpTest__SignInt32()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _clsVar1), ref Unsafe.As<Int32, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _clsVar2), ref Unsafe.As<Int32, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__SignInt32()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _fld1), ref Unsafe.As<Int32, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<Int32>, byte>(ref _fld2), ref Unsafe.As<Int32, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (int)(random.Next(int.MinValue + 1, int.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (int)(random.Next(int.MinValue, int.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<Int32, Int32, Int32>(_data1, _data2, new Int32[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.Sign(
+                Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.Sign(
+                Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.Sign(
+                Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<Int32>), typeof(Vector128<Int32>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<Int32>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.Sign(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<Int32>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<Int32>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((Int32*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((Int32*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((Int32*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__SignInt32();
+            var result = Ssse3.Sign(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.Sign(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<Int32> left, Vector128<Int32> right, void* result, [CallerMemberName] string method = "")
+        {
+            Int32[] inArray1 = new Int32[Op1ElementCount];
+            Int32[] inArray2 = new Int32[Op2ElementCount];
+            Int32[] outArray = new Int32[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, 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 = "")
+        {
+            Int32[] inArray1 = new Int32[Op1ElementCount];
+            Int32[] inArray2 = new Int32[Op2ElementCount];
+            Int32[] outArray = new Int32[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Int32, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(Int32[] left, Int32[] right, Int32[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != (right[0] < 0 ? (int)(-left[0]) : (right[0] > 0 ? left[0] : 0)))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != (right[i] < 0 ? (int)(-left[i]) : (right[i] > 0 ? left[i] : 0)))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Sign)}<Int32>(Vector128<Int32>, Vector128<Int32>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.SByte.cs b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Sign.SByte.cs
new file mode 100644 (file)
index 0000000..9947f0e
--- /dev/null
@@ -0,0 +1,330 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx  *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make    *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file.                                             *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+    public static partial class Program
+    {
+        private static void SignSByte()
+        {
+            var test = new SimpleBinaryOpTest__SignSByte();
+
+            if (test.IsSupported)
+            {
+                // Validates basic functionality works, using Unsafe.Read
+                test.RunBasicScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates basic functionality works, using Load
+                    test.RunBasicScenario_Load();
+
+                    // Validates basic functionality works, using LoadAligned
+                    test.RunBasicScenario_LoadAligned();
+                }
+
+                // Validates calling via reflection works, using Unsafe.Read
+                test.RunReflectionScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates calling via reflection works, using Load
+                    test.RunReflectionScenario_Load();
+
+                    // Validates calling via reflection works, using LoadAligned
+                    test.RunReflectionScenario_LoadAligned();
+                }
+
+                // Validates passing a static member works
+                test.RunClsVarScenario();
+
+                // Validates passing a local works, using Unsafe.Read
+                test.RunLclVarScenario_UnsafeRead();
+
+                if (Sse2.IsSupported)
+                {
+                    // Validates passing a local works, using Load
+                    test.RunLclVarScenario_Load();
+
+                    // Validates passing a local works, using LoadAligned
+                    test.RunLclVarScenario_LoadAligned();
+                }
+
+                // Validates passing the field of a local works
+                test.RunLclFldScenario();
+
+                // Validates passing an instance member works
+                test.RunFldScenario();
+            }
+            else
+            {
+                // Validates we throw on unsupported hardware
+                test.RunUnsupportedScenario();
+            }
+
+            if (!test.Succeeded)
+            {
+                throw new Exception("One or more scenarios did not complete as expected.");
+            }
+        }
+    }
+
+    public sealed unsafe class SimpleBinaryOpTest__SignSByte
+    {
+        private const int VectorSize = 16;
+
+        private const int Op1ElementCount = VectorSize / sizeof(SByte);
+        private const int Op2ElementCount = VectorSize / sizeof(SByte);
+        private const int RetElementCount = VectorSize / sizeof(SByte);
+
+        private static SByte[] _data1 = new SByte[Op1ElementCount];
+        private static SByte[] _data2 = new SByte[Op2ElementCount];
+
+        private static Vector128<SByte> _clsVar1;
+        private static Vector128<SByte> _clsVar2;
+
+        private Vector128<SByte> _fld1;
+        private Vector128<SByte> _fld2;
+
+        private SimpleBinaryOpTest__DataTable<SByte, SByte, SByte> _dataTable;
+
+        static SimpleBinaryOpTest__SignSByte()
+        {
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _clsVar1), ref Unsafe.As<SByte, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _clsVar2), ref Unsafe.As<SByte, byte>(ref _data2[0]), VectorSize);
+        }
+
+        public SimpleBinaryOpTest__SignSByte()
+        {
+            Succeeded = true;
+
+            var random = new Random();
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _fld1), ref Unsafe.As<SByte, byte>(ref _data1[0]), VectorSize);
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<Vector128<SByte>, byte>(ref _fld2), ref Unsafe.As<SByte, byte>(ref _data2[0]), VectorSize);
+
+            for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = (sbyte)(random.Next(sbyte.MinValue + 1, sbyte.MaxValue)); }
+            for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (sbyte)(random.Next(sbyte.MinValue, sbyte.MaxValue)); }
+            _dataTable = new SimpleBinaryOpTest__DataTable<SByte, SByte, SByte>(_data1, _data2, new SByte[RetElementCount], VectorSize);
+        }
+
+        public bool IsSupported => Ssse3.IsSupported;
+
+        public bool Succeeded { get; set; }
+
+        public void RunBasicScenario_UnsafeRead()
+        {
+            var result = Ssse3.Sign(
+                Unsafe.Read<Vector128<SByte>>(_dataTable.inArray1Ptr),
+                Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr)
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_Load()
+        {
+            var result = Ssse3.Sign(
+                Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunBasicScenario_LoadAligned()
+        {
+            var result = Ssse3.Sign(
+                Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)),
+                Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr))
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_UnsafeRead()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<SByte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Unsafe.Read<Vector128<SByte>>(_dataTable.inArray1Ptr),
+                                        Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr)
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<SByte>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_Load()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<SByte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<SByte>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunReflectionScenario_LoadAligned()
+        {
+            var result = typeof(Ssse3).GetMethod(nameof(Ssse3.Sign), new Type[] { typeof(Vector128<SByte>), typeof(Vector128<SByte>) })
+                                     .Invoke(null, new object[] {
+                                        Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr)),
+                                        Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr))
+                                     });
+
+            Unsafe.Write(_dataTable.outArrayPtr, (Vector128<SByte>)(result));
+            ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+        }
+
+        public void RunClsVarScenario()
+        {
+            var result = Ssse3.Sign(
+                _clsVar1,
+                _clsVar2
+            );
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_UnsafeRead()
+        {
+            var left = Unsafe.Read<Vector128<SByte>>(_dataTable.inArray1Ptr);
+            var right = Unsafe.Read<Vector128<SByte>>(_dataTable.inArray2Ptr);
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_Load()
+        {
+            var left = Sse2.LoadVector128((SByte*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadVector128((SByte*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclVarScenario_LoadAligned()
+        {
+            var left = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray1Ptr));
+            var right = Sse2.LoadAlignedVector128((SByte*)(_dataTable.inArray2Ptr));
+            var result = Ssse3.Sign(left, right);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(left, right, _dataTable.outArrayPtr);
+        }
+
+        public void RunLclFldScenario()
+        {
+            var test = new SimpleBinaryOpTest__SignSByte();
+            var result = Ssse3.Sign(test._fld1, test._fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunFldScenario()
+        {
+            var result = Ssse3.Sign(_fld1, _fld2);
+
+            Unsafe.Write(_dataTable.outArrayPtr, result);
+            ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+        }
+
+        public void RunUnsupportedScenario()
+        {
+            Succeeded = false;
+
+            try
+            {
+                RunBasicScenario_UnsafeRead();
+            }
+            catch (PlatformNotSupportedException)
+            {
+                Succeeded = true;
+            }
+        }
+
+        private void ValidateResult(Vector128<SByte> left, Vector128<SByte> right, void* result, [CallerMemberName] string method = "")
+        {
+            SByte[] inArray1 = new SByte[Op1ElementCount];
+            SByte[] inArray2 = new SByte[Op2ElementCount];
+            SByte[] outArray = new SByte[RetElementCount];
+
+            Unsafe.Write(Unsafe.AsPointer(ref inArray1[0]), left);
+            Unsafe.Write(Unsafe.AsPointer(ref inArray2[0]), right);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, 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 = "")
+        {
+            SByte[] inArray1 = new SByte[Op1ElementCount];
+            SByte[] inArray2 = new SByte[Op2ElementCount];
+            SByte[] outArray = new SByte[RetElementCount];
+
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(left), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(right), VectorSize);
+            Unsafe.CopyBlockUnaligned(ref Unsafe.As<SByte, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), VectorSize);
+
+            ValidateResult(inArray1, inArray2, outArray, method);
+        }
+
+        private void ValidateResult(SByte[] left, SByte[] right, SByte[] result, [CallerMemberName] string method = "")
+        {
+            if (result[0] != (right[0] < 0 ? (sbyte)(-left[0]) : (right[0] > 0 ? left[0] : 0)))
+            {
+                Succeeded = false;
+            }
+            else
+            {
+                for (var i = 1; i < RetElementCount; i++)
+                {
+                    if (result[i] != (right[i] < 0 ? (sbyte)(-left[i]) : (right[i] > 0 ? left[i] : 0)))
+                    {
+                        Succeeded = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!Succeeded)
+            {
+                Console.WriteLine($"{nameof(Ssse3)}.{nameof(Ssse3.Sign)}<SByte>(Vector128<SByte>, Vector128<SByte>): {method} failed:");
+                Console.WriteLine($"    left: ({string.Join(", ", left)})");
+                Console.WriteLine($"   right: ({string.Join(", ", right)})");
+                Console.WriteLine($"  result: ({string.Join(", ", result)})");
+                Console.WriteLine();
+            }
+        }
+    }
+}
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_r.csproj
new file mode 100644 (file)
index 0000000..fc18de2
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize></Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Abs.Byte.cs" />
+    <Compile Include="Abs.UInt16.cs" />
+    <Compile Include="Abs.UInt32.cs" />
+    <Compile Include="HorizontalAdd.Int16.cs" />
+    <Compile Include="HorizontalAdd.Int32.cs" />
+    <Compile Include="HorizontalAddSaturate.Int16.cs" />
+    <Compile Include="HorizontalSubtract.Int16.cs" />
+    <Compile Include="HorizontalSubtract.Int32.cs" />
+    <Compile Include="HorizontalSubtractSaturate.Int16.cs" />
+    <Compile Include="MultiplyAddAdjacent.Int16.cs" />
+    <Compile Include="MultiplyHighRoundScale.Int16.cs" />
+    <Compile Include="Shuffle.SByte.cs" />
+    <Compile Include="Sign.SByte.cs" />
+    <Compile Include="Sign.Int16.cs" />
+    <Compile Include="Sign.Int32.cs" />
+    <Compile Include="Program.Ssse3.cs" />
+    <Compile Include="..\Shared\Program.cs" />
+    <Compile Include="..\Shared\HorizontalBinOpTest_DataTable.cs" />
+    <Compile Include="..\Shared\SimpleBinOpTest_DataTable.cs" />
+    <Compile Include="..\Shared\SimpleUnOpTest_DataTable.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Ssse3/Ssse3_ro.csproj
new file mode 100644 (file)
index 0000000..8bad57d
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Abs.Byte.cs" />
+    <Compile Include="Abs.UInt16.cs" />
+    <Compile Include="Abs.UInt32.cs" />
+    <Compile Include="HorizontalAdd.Int16.cs" />
+    <Compile Include="HorizontalAdd.Int32.cs" />
+    <Compile Include="HorizontalAddSaturate.Int16.cs" />
+    <Compile Include="HorizontalSubtract.Int16.cs" />
+    <Compile Include="HorizontalSubtract.Int32.cs" />
+    <Compile Include="HorizontalSubtractSaturate.Int16.cs" />
+    <Compile Include="MultiplyAddAdjacent.Int16.cs" />
+    <Compile Include="MultiplyHighRoundScale.Int16.cs" />
+    <Compile Include="Shuffle.SByte.cs" />
+    <Compile Include="Sign.SByte.cs" />
+    <Compile Include="Sign.Int16.cs" />
+    <Compile Include="Sign.Int32.cs" />
+    <Compile Include="Program.Ssse3.cs" />
+    <Compile Include="..\Shared\Program.cs" />
+    <Compile Include="..\Shared\HorizontalBinOpTest_DataTable.cs" />
+    <Compile Include="..\Shared\SimpleBinOpTest_DataTable.cs" />
+    <Compile Include="..\Shared\SimpleUnOpTest_DataTable.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>