Repository: dotnet/runtime
Pull request #114087: [mono][interpreter] Make nint and nuint work for interpreter simd intrinsics
Author: login: lewing
Labels: name: area-Codegen-Interpreter-mono
Created: 2025-03-31T22:13:26Z | Merged: 2025-04-03T18:46:34Z

PackedSimd is currently broken in the interpreter with nint and nuint types because of this, and Vector128 fails to find the intrinsics.  Additionally `Vector128<nuint>` is used in a potential hot path. https://github.com/dotnet/runtime/blob/4b16dce6097aa70e09bb82e27c5055421cf74ded/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs#L293

fixes https://github.com/dotnet/runtime/issues/114088
Also fixes PackedSimd.Store*, and ConvertToDoubleLower interpreter intrinsics identified by the tests


Diff:
diff --git a/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj b/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj
index e956491c0a63b1..5d98f157e174a3 100644
--- a/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj
+++ b/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj
@@ -13,6 +13,7 @@
     <Compile Include="Vectors\Vector256Tests.cs" />
     <Compile Include="Vectors\Vector512Tests.cs" />
     <Compile Include="Vectors\Vector64Tests.cs" />
+    <Compile Include="Wasm\PackedSimdTests.cs" />
     <Compile Include="$(CommonTestPath)System\GenericMathTestMemberData.cs" Link="Common\System\GenericMathTestMemberData.cs" />
   </ItemGroup>
   <ItemGroup>
diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs
new file mode 100644
index 00000000000000..b3694d7e8cf771
--- /dev/null
+++ b/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs
@@ -0,0 +1,524 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.Wasm;
+using System.Tests;
+using Xunit;
+
+namespace System.Runtime.Intrinsics.Wasm.Tests
+{
+    [PlatformSpecific(TestPlatforms.Browser)]
+    public sealed class PackedSimdTests
+    {
+        [Fact]
+        public unsafe void BasicArithmeticTest()
+        {
+            var v1 = Vector128.Create(1, 2, 3, 4);
+            var v2 = Vector128.Create(5, 6, 7, 8);
+
+            var addResult = PackedSimd.Add(v1, v2);
+            var subResult = PackedSimd.Subtract(v1, v2);
+            var mulResult = PackedSimd.Multiply(v1, v2);
+
+            Assert.Equal(Vector128.Create(6, 8, 10, 12), addResult);
+            Assert.Equal(Vector128.Create(-4, -4, -4, -4), subResult);
+            Assert.Equal(Vector128.Create(5, 12, 21, 32), mulResult);
+        }
+
+        [Fact]
+        public unsafe void BitwiseOperationsTest()
+        {
+            var v1 = Vector128.Create(0b1100, 0b1010, 0b1110, 0b1111);
+            var v2 = Vector128.Create(0b1010, 0b1100, 0b0011, 0b0101);
+
+            var andResult = PackedSimd.And(v1, v2);
+            var orResult = PackedSimd.Or(v1, v2);
+            var xorResult = PackedSimd.Xor(v1, v2);
+
+            Assert.Equal(Vector128.Create(0b1000, 0b1000, 0b0010, 0b0101), andResult);
+            Assert.Equal(Vector128.Create(0b1110, 0b1110, 0b1111, 0b1111), orResult);
+            Assert.Equal(Vector128.Create(0b0110, 0b0110, 0b1101, 0b1010), xorResult);
+        }
+
+        [Fact]
+        public unsafe void ShiftOperationsTest()
+        {
+            var v = Vector128.Create(16, -16, 32, -32);
+
+            var leftShift = PackedSimd.ShiftLeft(v, 2);
+            var rightShiftArith = PackedSimd.ShiftRightArithmetic(v, 2);
+            var rightShiftLogical = PackedSimd.ShiftRightLogical(v, 2);
+
+            Assert.Equal(Vector128.Create(64, -64, 128, -128), leftShift);
+            Assert.Equal(Vector128.Create(4, -4, 8, -8), rightShiftArith);
+            Assert.Equal(Vector128.Create(4, 1073741820, 8, 1073741816), rightShiftLogical);
+        }
+
+        [Fact]
+        public unsafe void ComparisonOperationsTest()
+        {
+            var v1 = Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f);
+            var v2 = Vector128.Create(4.0f, 3.0f, 2.0f, 1.0f);
+
+            var minResult = PackedSimd.Min(v1, v2);
+            var maxResult = PackedSimd.Max(v1, v2);
+
+            Assert.Equal(Vector128.Create(1.0f, 2.0f, 2.0f, 1.0f), minResult);
+            Assert.Equal(Vector128.Create(4.0f, 3.0f, 3.0f, 4.0f), maxResult);
+        }
+
+        [Fact]
+        public unsafe void FloatingPointOperationsTest()
+        {
+            var v = Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f);
+
+            var sqrtResult = PackedSimd.Sqrt(v);
+            var ceilResult = PackedSimd.Ceiling(v);
+            var floorResult = PackedSimd.Floor(v);
+
+            Assert.Equal(Vector128.Create(2.0f, 3.0f, 4.0f, 5.0f), sqrtResult);
+            Assert.Equal(Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f), ceilResult);
+            Assert.Equal(Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f), floorResult);
+        }
+
+        [Fact]
+        public unsafe void LoadStoreTest()
+        {
+            int[] values = new int[] { 1, 2, 3, 4 };
+            fixed (int* ptr = values)
+            {
+                var loaded = PackedSimd.LoadVector128(ptr);
+                Assert.Equal(Vector128.Create(1, 2, 3, 4), loaded);
+
+                int[] storeTarget = new int[4];
+                fixed (int* storePtr = storeTarget)
+                {
+                    PackedSimd.Store(storePtr, loaded);
+                    Assert.Equal(values, storeTarget);
+                }
+            }
+        }
+
+        [Fact]
+        public unsafe void ExtractInsertScalarTest()
+        {
+            var v = Vector128.Create(1, 2, 3, 4);
+
+            int extracted = PackedSimd.ExtractScalar(v, 2);
+            Assert.Equal(3, extracted);
+
+            var modified = PackedSimd.ReplaceScalar(v, 2, 10);
+            Assert.Equal(Vector128.Create(1, 2, 10, 4), modified);
+        }
+
+        [Fact]
+        public unsafe void SaturatingArithmeticTest()
+        {
+            var v1 = Vector128.Create((byte)250, (byte)251, (byte)252, (byte)253, (byte)254, (byte)255, (byte)255, (byte)255,
+                                    (byte)250, (byte)251, (byte)252, (byte)253, (byte)254, (byte)255, (byte)255, (byte)255);
+            var v2 = Vector128.Create((byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10,
+                                    (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10);
+
+            var addSat = PackedSimd.AddSaturate(v1, v2);
+            var subSat = PackedSimd.SubtractSaturate(v1, v2);
+
+            // Verify saturation at 255 for addition
+            Assert.Equal(Vector128.Create((byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255,
+                                        (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255), addSat);
+
+            // Verify expected subtraction results
+            Assert.Equal(Vector128.Create((byte)240, (byte)241, (byte)242, (byte)243, (byte)244, (byte)245, (byte)245, (byte)245,
+                                        (byte)240, (byte)241, (byte)242, (byte)243, (byte)244, (byte)245, (byte)245, (byte)245), subSat);
+        }
+
+        [Fact]
+        public unsafe void WideningOperationsTest()
+        {
+            var v = Vector128.Create((short)1000, (short)2000, (short)3000, (short)4000,
+                                   (short)5000, (short)6000, (short)7000, (short)8000);
+
+            var lowerWidened = PackedSimd.SignExtendWideningLower(v);
+            var upperWidened = PackedSimd.SignExtendWideningUpper(v);
+
+            Assert.Equal(Vector128.Create(1000, 2000, 3000, 4000), lowerWidened);
+            Assert.Equal(Vector128.Create(5000, 6000, 7000, 8000), upperWidened);
+        }
+
+        [Fact]
+        public unsafe void SwizzleTest()
+        {
+            var v = Vector128.Create((byte)1, (byte)2, (byte)3, (byte)4, (byte)5, (byte)6, (byte)7, (byte)8,
+                                   (byte)9, (byte)10, (byte)11, (byte)12, (byte)13, (byte)14, (byte)15, (byte)16);
+            var indices = Vector128.Create((byte)3, (byte)2, (byte)1, (byte)0, (byte)7, (byte)6, (byte)5, (byte)4,
+                                        (byte)11, (byte)10, (byte)9, (byte)8, (byte)15, (byte)14, (byte)13, (byte)12);
+
+            var swizzled = PackedSimd.Swizzle(v, indices);
+
+            Assert.Equal(Vector128.Create((byte)4, (byte)3, (byte)2, (byte)1, (byte)8, (byte)7, (byte)6, (byte)5,
+                                        (byte)12, (byte)11, (byte)10, (byte)9, (byte)16, (byte)15, (byte)14, (byte)13), swizzled);
+        }
+
+        [Fact]
+        public unsafe void LoadScalarAndSplatTest()
+        {
+            int value = 42;
+            float fValue = 3.14f;
+
+            int* intPtr = &value;
+            float* floatPtr = &fValue;
+
+            var intSplat = PackedSimd.LoadScalarAndSplatVector128(intPtr);
+            var floatSplat = PackedSimd.LoadScalarAndSplatVector128(floatPtr);
+
+            Assert.Equal(Vector128.Create(42, 42, 42, 42), intSplat);
+            Assert.Equal(Vector128.Create(3.14f, 3.14f, 3.14f, 3.14f), floatSplat);
+        }
+
+        [Fact]
+        public unsafe void LoadWideningTest()
+        {
+            byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+            fixed (byte* ptr = bytes)
+            {
+                var widened = PackedSimd.LoadWideningVector128(ptr);
+                Assert.Equal(Vector128.Create((ushort)1, (ushort)2, (ushort)3, (ushort)4,
+                                           (ushort)5, (ushort)6, (ushort)7, (ushort)8), widened);
+            }
+        }
+
+        [Fact]
+        public unsafe void StoreSelectedScalarTest()
+        {
+            var v = Vector128.Create(1, 2, 3, 4);
+            int value = 0;
+            int* ptr = &value;
+
+            PackedSimd.StoreSelectedScalar(ptr, v, 2);
+            Assert.Equal(3, value);
+        }
+
+        [Fact]
+        public unsafe void LoadScalarAndInsertTest()
+        {
+            var v = Vector128.Create(1, 2, 3, 4);
+            int newValue = 42;
+            int* ptr = &newValue;
+
+            var result = PackedSimd.LoadScalarAndInsert(ptr, v, 2);
+            Assert.Equal(Vector128.Create(1, 2, 42, 4), result);
+        }
+
+        [Fact]
+        public unsafe void ConversionTest()
+        {
+            var intVector = Vector128.Create(1, 2, 3, 4);
+            var floatVector = Vector128.Create(1.5f, 2.5f, 3.5f, 4.5f);
+            var doubleVector = Vector128.Create(1.5, 2.5);
+
+            var intToFloat = PackedSimd.ConvertToSingle(intVector);
+            var floatToDouble = PackedSimd.ConvertToDoubleLower(floatVector);
+
+            Assert.Equal(Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f), intToFloat);
+            Assert.Equal(Vector128.Create(1.5, 2.5), floatToDouble);
+        }
+
+        [Fact]
+        public unsafe void AddPairwiseWideningTest()
+        {
+            var bytes = Vector128.Create((byte)1, (byte)2, (byte)3, (byte)4,
+                                       (byte)5, (byte)6, (byte)7, (byte)8,
+                                       (byte)9, (byte)10, (byte)11, (byte)12,
+                                       (byte)13, (byte)14, (byte)15, (byte)16);
+
+            var widened = PackedSimd.AddPairwiseWidening(bytes);
+
+            Assert.Equal(Vector128.Create((ushort)3, (ushort)7, (ushort)11, (ushort)15,
+                                        (ushort)19, (ushort)23, (ushort)27, (ushort)31), widened);
+        }
+
+        [Fact]
+        public unsafe void MultiplyWideningTest()
+        {
+            var shorts = Vector128.Create((short)10, (short)20, (short)30, (short)40,
+                                        (short)50, (short)60, (short)70, (short)80);
+            var multiplier = Vector128.Create((short)2, (short)2, (short)2, (short)2,
+                                            (short)2, (short)2, (short)2, (short)2);
+
+            var lowerResult = PackedSimd.MultiplyWideningLower(shorts, multiplier);
+            var upperResult = PackedSimd.MultiplyWideningUpper(shorts, multiplier);
+
+            Assert.Equal(Vector128.Create(20, 40, 60, 80), lowerResult);
+            Assert.Equal(Vector128.Create(100, 120, 140, 160), upperResult);
+        }
+
+        [Fact]
+        public unsafe void DotProductTest()
+        {
+            var v1 = Vector128.Create((short)1, (short)2, (short)3, (short)4,
+                                    (short)5, (short)6, (short)7, (short)8);
+            var v2 = Vector128.Create((short)2, (short)2, (short)2, (short)2,
+                                    (short)2, (short)2, (short)2, (short)2);
+
+            var dot = PackedSimd.Dot(v1, v2);
+
+            // Each pair of values is multiplied and added:
+            // (1*2 + 2*2) = 6 for first int
+            // (3*2 + 4*2) = 14 for second int
+            // (5*2 + 6*2) = 22 for third int
+            // (7*2 + 8*2) = 30 for fourth int
+            Assert.Equal(Vector128.Create(6, 14, 22, 30), dot);
+        }
+
+        [Fact]
+        public unsafe void FloatingPointNegationTest()
+        {
+            var v = Vector128.Create(1.0f, -2.0f, 3.0f, -4.0f);
+            var d = Vector128.Create(1.0, -2.0);
+
+            var negatedFloat = PackedSimd.Negate(v);
+            var negatedDouble = PackedSimd.Negate(d);
+
+            Assert.Equal(Vector128.Create(-1.0f, 2.0f, -3.0f, 4.0f), negatedFloat);
+            Assert.Equal(Vector128.Create(-1.0, 2.0), negatedDouble);
+        }
+
+        [Fact]
+        public unsafe void FloatingPointAbsTest()
+        {
+            var v = Vector128.Create(-1.0f, 2.0f, -3.0f, 4.0f);
+            var d = Vector128.Create(-1.0, 2.0);
+
+            var absFloat = PackedSimd.Abs(v);
+            var absDouble = PackedSimd.Abs(d);
+
+            Assert.Equal(Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f), absFloat);
+            Assert.Equal(Vector128.Create(1.0, 2.0), absDouble);
+        }
+
+        [Fact]
+        public unsafe void FloatingPointDivisionTest()
+        {
+            var v1 = Vector128.Create(2.0f, 4.0f, 6.0f, 8.0f);
+            var v2 = Vector128.Create(2.0f, 2.0f, 2.0f, 2.0f);
+            var d1 = Vector128.Create(2.0, 4.0);
+            var d2 = Vector128.Create(2.0, 2.0);
+
+            var divFloat = PackedSimd.Divide(v1, v2);
+            var divDouble = PackedSimd.Divide(d1, d2);
+
+            Assert.Equal(Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f), divFloat);
+            Assert.Equal(Vector128.Create(1.0, 2.0), divDouble);
+        }
+
+        [Fact]
+        public unsafe void IntegerAbsTest()
+        {
+            var bytes = Vector128.Create((sbyte)-1, (sbyte)2, (sbyte)-3, (sbyte)4,
+                                       (sbyte)-5, (sbyte)6, (sbyte)-7, (sbyte)8,
+                                       (sbyte)-9, (sbyte)10, (sbyte)-11, (sbyte)12,
+                                       (sbyte)-13, (sbyte)14, (sbyte)-15, (sbyte)16);
+            var shorts = Vector128.Create((short)-1, (short)2, (short)-3, (short)4,
+                                        (short)-5, (short)6, (short)-7, (short)8);
+            var ints = Vector128.Create(-1, 2, -3, 4);
+
+            var absBytes = PackedSimd.Abs(bytes);
+            var absShorts = PackedSimd.Abs(shorts);
+
+            Assert.Equal(Vector128.Create((sbyte)1, (sbyte)2, (sbyte)3, (sbyte)4,
+                                        (sbyte)5, (sbyte)6, (sbyte)7, (sbyte)8,
+                                        (sbyte)9, (sbyte)10, (sbyte)11, (sbyte)12,
+                                        (sbyte)13, (sbyte)14, (sbyte)15, (sbyte)16), absBytes);
+            Assert.Equal(Vector128.Create((short)1, (short)2, (short)3, (short)4,
+                                        (short)5, (short)6, (short)7, (short)8), absShorts);
+        }
+
+        [Fact]
+        public unsafe void AverageRoundedTest()
+        {
+            var bytes1 = Vector128.Create((byte)1, (byte)3, (byte)5, (byte)7,
+                                        (byte)9, (byte)11, (byte)13, (byte)15,
+                                        (byte)17, (byte)19, (byte)21, (byte)23,
+                                        (byte)25, (byte)27, (byte)29, (byte)31);
+            var bytes2 = Vector128.Create((byte)3, (byte)5, (byte)7, (byte)9,
+                                        (byte)11, (byte)13, (byte)15, (byte)17,
+                                        (byte)19, (byte)21, (byte)23, (byte)25,
+                                        (byte)27, (byte)29, (byte)31, (byte)33);
+
+            var avgBytes = PackedSimd.AverageRounded(bytes1, bytes2);
+
+            // Average is rounded up: (a + b + 1) >> 1
+            Assert.Equal(Vector128.Create((byte)2, (byte)4, (byte)6, (byte)8,
+                                        (byte)10, (byte)12, (byte)14, (byte)16,
+                                        (byte)18, (byte)20, (byte)22, (byte)24,
+                                        (byte)26, (byte)28, (byte)30, (byte)32), avgBytes);
+        }
+
+        [Fact]
+        public unsafe void MinMaxSignedUnsignedTest()
+        {
+            var signedBytes = Vector128.Create((sbyte)-1, (sbyte)2, (sbyte)-3, (sbyte)4,
+                                             (sbyte)-5, (sbyte)6, (sbyte)-7, (sbyte)8,
+                                             (sbyte)-9, (sbyte)10, (sbyte)-11, (sbyte)12,
+                                             (sbyte)-13, (sbyte)14, (sbyte)-15, (sbyte)16);
+
+            var unsignedBytes = Vector128.Create((byte)255, (byte)2, (byte)253, (byte)4,
+                                               (byte)251, (byte)6, (byte)249, (byte)8,
+                                               (byte)247, (byte)10, (byte)245, (byte)12,
+                                               (byte)243, (byte)14, (byte)241, (byte)16);
+
+            var signedMin = PackedSimd.Min(signedBytes, signedBytes.WithElement(0, (sbyte)0));
+            var unsignedMin = PackedSimd.Min(unsignedBytes, unsignedBytes.WithElement(0, (byte)0));
+
+            // Verify different comparison behavior for signed vs unsigned
+            Assert.Equal((sbyte)-1, signedMin.GetElement(0));
+            Assert.Equal((byte)0, unsignedMin.GetElement(0));
+        }
+
+        [Fact]
+        public unsafe void LoadScalarAndSplatInfinityTest()
+        {
+            float fInf = float.PositiveInfinity;
+            double dInf = double.PositiveInfinity;
+
+            float* fPtr = &fInf;
+            double* dPtr = &dInf;
+
+            var floatSplat = PackedSimd.LoadScalarAndSplatVector128(fPtr);
+            var doubleSplat = PackedSimd.LoadScalarAndSplatVector128(dPtr);
+
+            for (int i = 0; i < 4; i++)
+            {
+                Assert.True(float.IsPositiveInfinity(floatSplat.GetElement(i)));
+            }
+
+            for (int i = 0; i < 2; i++)
+            {
+                Assert.True(double.IsPositiveInfinity(doubleSplat.GetElement(i)));
+            }
+        }
+
+        [Fact]
+        public unsafe void FloatingPointTruncateTest()
+        {
+            var v1 = Vector128.Create(1.7f, -2.3f, 3.5f, -4.8f);
+            var d1 = Vector128.Create(1.7, -2.3);
+
+            var truncFloat = PackedSimd.Truncate(v1);
+            var truncDouble = PackedSimd.Truncate(d1);
+
+            Assert.Equal(Vector128.Create(1.0f, -2.0f, 3.0f, -4.0f), truncFloat);
+            Assert.Equal(Vector128.Create(1.0, -2.0), truncDouble);
+        }
+
+        [Fact]
+        public unsafe void ComparisonWithNaNTest()
+        {
+            var v1 = Vector128.Create(1.0f, float.NaN, 3.0f, float.PositiveInfinity);
+            var v2 = Vector128.Create(float.NegativeInfinity, 2.0f, float.NaN, 4.0f);
+
+            var minResult = PackedSimd.Min(v1, v2);
+            var maxResult = PackedSimd.Max(v1, v2);
+
+            // IEEE 754 rules: if either operand is NaN, the result should be NaN
+            Assert.True(float.IsNaN(minResult.GetElement(1)));
+            Assert.True(float.IsNaN(maxResult.GetElement(2)));
+            Assert.Equal(float.NegativeInfinity, minResult.GetElement(0));
+            Assert.Equal(float.PositiveInfinity, maxResult.GetElement(3));
+        }
+
+        [Fact]
+        public unsafe void NativeIntegerArithmeticTest()
+        {
+            var v1 = Vector128.Create([(nint)1, (nint)2, (nint)3, (nint)4]);
+            var v2 = Vector128.Create([(nint)5, (nint)6, (nint)7, (nint)8]);
+
+            var addResult = PackedSimd.Add(v1, v2);
+            var subResult = PackedSimd.Subtract(v1, v2);
+            var mulResult = PackedSimd.Multiply(v1, v2);
+
+            Assert.Equal(Vector128.Create([(nint)6, (nint)8, (nint)10, (nint)12]), addResult);
+            Assert.Equal(Vector128.Create([(nint)(-4), (nint)(-4), (nint)(-4), (nint)(-4)]), subResult);
+            Assert.Equal(Vector128.Create([(nint)5, (nint)12, (nint)21, (nint)32]), mulResult);
+        }
+
+        [Fact]
+        public unsafe void NativeUnsignedIntegerArithmeticTest()
+        {
+            var v1 = Vector128.Create([(nuint)1, (nuint)2, (nuint)3, (nuint)4]);
+            var v2 = Vector128.Create([(nuint)5, (nuint)6, (nuint)7, (nuint)8]);
+
+            var addResult = PackedSimd.Add(v1, v2);
+            var subResult = PackedSimd.Subtract(v1, v2);
+            var mulResult = PackedSimd.Multiply(v1, v2);
+
+            Assert.Equal(Vector128.Create([(nuint)6, (nuint)8, (nuint)10, (nuint)12]), addResult);
+            Assert.Equal(Vector128.Create([unchecked((nuint)(-4)), unchecked((nuint)(-4)), unchecked((nuint)(-4)), unchecked((nuint)(-4))]), subResult);
+            Assert.Equal(Vector128.Create([(nuint)5, (nuint)12, (nuint)21, (nuint)32]), mulResult);
+        }
+
+        [Fact]
+        public unsafe void NativeIntegerLoadStoreTest()
+        {
+            nint[] values = new nint[] { 1, 2, 3, 4 };
+            fixed (nint* ptr = values)
+            {
+                var loaded = PackedSimd.LoadVector128(ptr);
+                Assert.Equal(Vector128.Create(values.AsSpan()), loaded);
+
+                nint[] storeTarget = new nint[4];
+                fixed (nint* storePtr = storeTarget)
+                {
+                    PackedSimd.Store(storePtr, loaded);
+                    Assert.Equal(values, storeTarget);
+                }
+            }
+        }
+
+        [Fact]
+        public unsafe void NativeUnsignedIntegerLoadStoreTest()
+        {
+            nuint[] values = new nuint[] { 1, 2, 3, 4 };
+            fixed (nuint* ptr = values)
+            {
+                var loaded = PackedSimd.LoadVector128(ptr);
+                Assert.Equal(Vector128.Create(values.AsSpan()), loaded);
+
+                nuint[] storeTarget = new nuint[4];
+                fixed (nuint* storePtr = storeTarget)
+                {
+                    PackedSimd.Store(storePtr, loaded);
+                    Assert.Equal(values, storeTarget);
+                }
+            }
+        }
+
+        [Fact]
+        public void NativeIntegerShiftTest()
+        {
+            var v = Vector128.Create([(nint)16, (nint)(-16), (nint)32, (nint)(-32)]);
+
+            var leftShift = PackedSimd.ShiftLeft(v, 2);
+            var rightShiftArith = PackedSimd.ShiftRightArithmetic(v, 2);
+            var rightShiftLogical = PackedSimd.ShiftRightLogical(v, 2);
+
+            Assert.Equal(Vector128.Create([(nint)64, (nint)(-64), (nint)128, (nint)(-128)]), leftShift);
+            Assert.Equal(Vector128.Create([(nint)4, (nint)(-4), (nint)8, (nint)(-8)]), rightShiftArith);
+            Assert.Equal(Vector128.Create([(nint)4, (nint)1073741820, (nint)8, (nint)1073741816]), rightShiftLogical);
+        }
+
+        [Fact]
+        public void NativeUnsignedIntegerShiftTest()
+        {
+            var v = Vector128.Create([(nuint)16, unchecked((nuint)(-16)), (nuint)32, unchecked((nuint)(-32))]);
+
+            var leftShift = PackedSimd.ShiftLeft(v, 2);
+            var rightShiftLogical = PackedSimd.ShiftRightLogical(v, 2);
+
+            Assert.Equal(Vector128.Create([(nuint)64, unchecked((nuint)(-64)), (nuint)128, unchecked((nuint)(-128))]), leftShift);
+            Assert.Equal(Vector128.Create([(nuint)4, (nuint)1073741820, (nuint)8, (nuint)1073741816]), rightShiftLogical);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mono/mono/mini/interp/interp-simd-intrins.def b/src/mono/mono/mini/interp/interp-simd-intrins.def
index d88e543af23471..197b3c269d6612 100644
--- a/src/mono/mono/mini/interp/interp-simd-intrins.def
+++ b/src/mono/mono/mini/interp/interp-simd-intrins.def
@@ -351,7 +351,7 @@ INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToSingle, U4, wasm_f32x4_convert_u32x4,
 INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToSingle, R8, wasm_f32x4_demote_f64x2_zero, 0x5e)
 INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToDoubleLower, I4, wasm_f64x2_convert_low_i32x4, 0xfe)
 INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToDoubleLower, U4, wasm_f64x2_convert_low_u32x4, 0xff)
-INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToDoubleLower, R8, wasm_f64x2_promote_low_f32x4, 0x5f)
+INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToDoubleLower, R4, wasm_f64x2_promote_low_f32x4, 0x5f)
 INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToInt32Saturate, R4, wasm_i32x4_trunc_sat_f32x4, 0xf8)
 INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToUInt32Saturate, R4, wasm_u32x4_trunc_sat_f32x4, 0xf9)
 INTERP_WASM_SIMD_INTRINSIC_V_V  (ConvertToInt32Saturate, R8, wasm_i32x4_trunc_sat_f64x2_zero, 0xfc)
diff --git a/src/mono/mono/mini/interp/transform-simd.c b/src/mono/mono/mini/interp/transform-simd.c
index 7a56b6e90ab87d..3ef90289648614 100644
--- a/src/mono/mono/mini/interp/transform-simd.c
+++ b/src/mono/mono/mini/interp/transform-simd.c
@@ -162,10 +162,29 @@ static guint16 sri_packedsimd_methods [] = {
 	SN_get_IsSupported,
 };
 
+static MonoTypeEnum 
+resolve_native_size (MonoTypeEnum type)
+{
+	if (type == MONO_TYPE_I)
+#if TARGET_SIZEOF_VOID_P == 4
+		return MONO_TYPE_I4;
+#else
+		return MONO_TYPE_I8;
+#endif
+	else if (type == MONO_TYPE_U)
+#if TARGET_SIZEOF_VOID_P == 4
+		return MONO_TYPE_U4;
+#else
+		return MONO_TYPE_U8;
+#endif
+	return type;
+
+}
 // Returns if opcode was added
 static gboolean
 emit_common_simd_operations (TransformData *td, int id, int atype, int vector_size, int arg_size, int scalar_arg, gint16 *simd_opcode, gint16 *simd_intrins)
 {
+	atype = resolve_native_size (atype);
 	switch (id) {
 		case SN_get_AllBitsSet: {
 			interp_add_ins (td, MINT_SIMD_V128_LDC);
@@ -563,6 +582,7 @@ emit_sri_vector128 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
 			else if (arg_size == 8) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I8_CREATE_SCALAR;
 			break;
 		case SN_Equals:
+			atype = resolve_native_size (atype);
 			simd_opcode = MINT_SIMD_INTRINS_P_PP;
 			if (atype == MONO_TYPE_I1 || atype == MONO_TYPE_U1) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I1_EQUALS;
 			else if (atype == MONO_TYPE_I2 || atype == MONO_TYPE_U2) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I2_EQUALS;
@@ -571,6 +591,7 @@ emit_sri_vector128 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
 			else if (atype == MONO_TYPE_R4) simd_intrins = INTERP_SIMD_INTRINSIC_V128_R4_EQUALS;
 			break;
 		case SN_EqualsAny:
+			atype = resolve_native_size (atype);
 			simd_opcode = MINT_SIMD_INTRINS_P_PP;
 			if (atype == MONO_TYPE_I1 || atype == MONO_TYPE_U1) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I1_EQUALS_ANY;
 			else if (atype == MONO_TYPE_I2 || atype == MONO_TYPE_U2) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I2_EQUALS_ANY;
@@ -621,6 +642,7 @@ emit_sri_vector128 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
 			break;
 		case SN_ShiftRightArithmetic:
 			g_assert (scalar_arg == 1);
+			atype = resolve_native_size (atype);
 			simd_opcode = MINT_SIMD_INTRINS_P_PP;
 			if (atype == MONO_TYPE_I1) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I1_RIGHT_SHIFT;
 			else if (atype == MONO_TYPE_I2) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I2_RIGHT_SHIFT;
@@ -845,6 +867,8 @@ emit_sn_vector4 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature *cs
 static gboolean
 packedsimd_type_matches (MonoTypeEnum type, int expected_type)
 {
+	type = resolve_native_size (type);
+
 	if (expected_type == PSIMD_ARGTYPE_ANY)
 		return TRUE;
 	else if (type == expected_type)
@@ -1034,7 +1058,12 @@ emit_sri_packedsimd (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
 
 	bool is_packedsimd = strcmp (m_class_get_name (cmethod->klass), "PackedSimd") == 0;
 	if (is_packedsimd) {
-		vector_klass = mono_class_from_mono_type_internal (csignature->ret);
+		if (csignature->ret->type == MONO_TYPE_VOID && csignature->param_count > 1 && mono_type_is_pointer (csignature->params [0])) {
+			// The Store* methods have a more complicated signature
+			vector_klass = mono_class_from_mono_type_internal (csignature->params [1]);
+		} else {
+			vector_klass = mono_class_from_mono_type_internal (csignature->ret);
+		}
 	} else {
 		if (csignature->ret->type == MONO_TYPE_GENERICINST) {
 			vector_klass = mono_class_from_mono_type_internal (csignature->ret);


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: Copilot wasn't able to review any files in this pull request.
<details>
<summary>Files not reviewed (1)</summary>

* **src/mono/mono/mini/interp/transform-simd.c**: Language not supported
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T20:32:20Z
user: login: steveisok
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-03T17:04:19Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T17:04:29Z
user: login: lewing
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T17:12:41Z

Review comments:
diff_hunk: @@ -563,6 +582,7 @@ emit_sri_vector128 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
 			else if (arg_size == 8) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I8_CREATE_SCALAR;
 			break;
 		case SN_Equals:
+			atype = resolve_native_size (atype);
path: src/mono/mono/mini/interp/transform-simd.c
user: login: kg
body: why in these individual cases instead of up top?
created_at: 2025-04-03T17:04:28Z
author_association: MEMBER
diff_hunk: @@ -563,6 +582,7 @@ emit_sri_vector128 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
 			else if (arg_size == 8) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I8_CREATE_SCALAR;
 			break;
 		case SN_Equals:
+			atype = resolve_native_size (atype);
path: src/mono/mono/mini/interp/transform-simd.c
user: login: lewing
body: If you look farther up in the function there are cases where atype is checked against the parameter type.  I just made a choice to be explicit about doing discarding the true type it was needed rather than introducing a new variable there.
created_at: 2025-04-03T17:12:41Z
author_association: MEMBER

PR comments:
user: login: dotnet-policy-service[bot]
created_at: 2025-03-31T22:13:59Z
body: Tagging subscribers to this area: @brzvlad, @kotlarmilos
See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
<!-- Policy app identification https://img.shields.io/static/v1?label=PullRequestIssueManagement. -->
author_association: CONTRIBUTOR
user: login: lewing
created_at: 2025-03-31T22:19:45Z
body: More fixes on the way
author_association: MEMBER
user: login: lewing
created_at: 2025-04-01T21:06:04Z
body: Need to add a test case
author_association: MEMBER
user: login: lewing
created_at: 2025-04-03T15:47:32Z
body: I will backport the runtime abort fixes to net 9 manually.

https://github.com/dotnet/runtime/pull/114218

author_association: MEMBER
user: login: lewing
created_at: 2025-04-03T18:45:06Z
body: /ba-g failures are unrelated
author_association: MEMBER