diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 0fe533692bf2e6..261009571f8cfa 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.Wasm; using System.Runtime.Intrinsics.X86; using System.Text; using System.Text.Unicode; @@ -243,7 +244,7 @@ public static char ToCharLower(int value) public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes, out int charsProcessed) { #if SYSTEM_PRIVATE_CORELIB - if (BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && + if (BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && chars.Length >= Vector128.Count * 2) { return TryDecodeFromUtf16_Vector128(chars, bytes, out charsProcessed); @@ -255,9 +256,10 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes #if SYSTEM_PRIVATE_CORELIB [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(PackedSimd))] public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span bytes, out int charsProcessed) { - Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported); + Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported); Debug.Assert(chars.Length <= bytes.Length * 2); Debug.Assert(chars.Length % 2 == 0); Debug.Assert(chars.Length >= Vector128.Count * 2); @@ -314,6 +316,12 @@ public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span shiftedNibbles = PackedSimd.ShiftLeft(nibbles, 4); + Vector128 zipped = PackedSimd.BitwiseSelect(nibbles, shiftedNibbles, Vector128.Create((ushort)0xFF00).AsByte()); + output = PackedSimd.AddPairwiseWidening(zipped).AsByte(); + } else { // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are live/dead diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index ba7d0fc99e0b00..47f20592da76ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -4296,12 +4296,17 @@ internal static Vector128 UnpackHigh(Vector128 left, Vector128 [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] [CompExactlyDependsOn(typeof(Sse2))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static Vector128 AddSaturate(Vector128 left, Vector128 right) { if (Sse2.IsSupported) { return Sse2.AddSaturate(left, right); } + else if (PackedSimd.IsSupported) + { + return PackedSimd.AddSaturate(left, right); + } else if (!AdvSimd.Arm64.IsSupported) { ThrowHelper.ThrowNotSupportedException(); @@ -4312,12 +4317,17 @@ internal static Vector128 AddSaturate(Vector128 left, Vector128 SubtractSaturate(Vector128 left, Vector128 right) { if (Sse2.IsSupported) { return Sse2.SubtractSaturate(left, right); } + else if (PackedSimd.IsSupported) + { + return PackedSimd.SubtractSaturate(left, right); + } else if (!AdvSimd.Arm64.IsSupported) { ThrowHelper.ThrowNotSupportedException(); @@ -4328,12 +4338,17 @@ internal static Vector128 SubtractSaturate(Vector128 left, Vector128 [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] [CompExactlyDependsOn(typeof(Sse2))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static Vector128 AddSaturate(Vector128 left, Vector128 right) { if (Sse2.IsSupported) { return Sse2.AddSaturate(left, right); } + else if (PackedSimd.IsSupported) + { + return PackedSimd.AddSaturate(left, right); + } else if (!AdvSimd.Arm64.IsSupported) { ThrowHelper.ThrowNotSupportedException(); diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs index e108db25883d7f..d8d4cfc87d4af7 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs @@ -1,7 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +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; @@ -12,6 +16,16 @@ namespace System.Runtime.Intrinsics.Wasm.Tests [PlatformSpecific(TestPlatforms.Browser)] public sealed class PackedSimdTests { + [Fact] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(PackedSimd))] + public unsafe void PackedSimdIsSupported() + { + MethodInfo methodInfo = typeof(PackedSimd).GetMethod("get_IsSupported"); + Assert.Equal(PackedSimd.IsSupported, methodInfo.Invoke(null, null)); + Assert.Equal(PackedSimd.IsSupported, Vector128.IsHardwareAccelerated); + Assert.True(PackedSimd.IsSupported); + } + [Fact] public unsafe void BasicArithmeticTest() { @@ -83,6 +97,113 @@ public unsafe void FloatingPointOperationsTest() Assert.Equal(Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f), floorResult); } + [Fact] + public unsafe void NotTests() + { + var v16 = Vector128.Create((byte)0b11001100); + var v8 = Vector128.Create((ushort)0b11110000_11001100); + var v4 = Vector128.Create((uint)0b11111111_00000000_11110000_00000000); + + var notResult16 = PackedSimd.Not(v16); + var notResult8 = PackedSimd.Not(v8); + var notResult4 = PackedSimd.Not(v4); + + Assert.Equal(Vector128.Create((byte)0b00110011), notResult16); + Assert.Equal(Vector128.Create((ushort)0b00001111_00110011), notResult8); + Assert.Equal(Vector128.Create((uint)0b00000000_11111111_00001111_11111111), notResult4); + var oc16 = ~v16; + var oc8 = ~v8; + var oc4 = ~v4; + + Assert.Equal(oc4, notResult4); + Assert.Equal(oc8, notResult8); + Assert.Equal(oc16, notResult16); + + Assert.Equal(Vector128.OnesComplement(v4), notResult4); + Assert.Equal(Vector128.OnesComplement(v8), notResult8); + Assert.Equal(Vector128.OnesComplement(v16), notResult16); + } + + [Fact] + public unsafe void BitwiseSelectTest() + { + // Test with integers + var mask = Vector128.Create(unchecked((int)0xFFFFFFFF), 0, unchecked((int)0xFFFFFFFF), 0); + var a = Vector128.Create(1, 2, 3, 4); + var b = Vector128.Create(5, 6, 7, 8); + + // Use the correct parameter order: left(a), right(b), select(mask) + var result = PackedSimd.BitwiseSelect(a, b, mask); + var v128result = Vector128.ConditionalSelect(mask, a, b); + // Where mask is all 1s, should select from a; where mask is all 0s, should select from b + Assert.Equal(Vector128.Create(1, 6, 3, 8), v128result); + Assert.Equal(v128result, result); + + // Test with bytes for more granular bit-level control + var byteMask = Vector128.Create((byte)0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00); + var byteA = Vector128.Create((byte)1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + var byteB = Vector128.Create((byte)17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + ); + + // Use the correct parameter order: left(byteA), right(byteB), select(byteMask) + var byteResult = PackedSimd.BitwiseSelect(byteA, byteB, byteMask); + Assert.Equal(Vector128.Create( + (byte)1, 18, 3, 20, 5, 22, 7, 24, + 9, 26, 11, 28, 13, 30, 15, 32 + ), byteResult); + + // Test with floats to ensure proper handling of floating-point data + var floatMask = Vector128.Create( + BitConverter.Int32BitsToSingle(unchecked((int)0xFFFFFFFF)), + BitConverter.Int32BitsToSingle(0), + BitConverter.Int32BitsToSingle(unchecked((int)0xFFFFFFFF)), + BitConverter.Int32BitsToSingle(0) + ); + var floatA = Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f); + var floatB = Vector128.Create(5.0f, 6.0f, 7.0f, 8.0f); + + // Use the correct parameter order: left(floatA), right(floatB), select(floatMask) + var floatResult = PackedSimd.BitwiseSelect(floatA, floatB, floatMask); + // Verify expected selection pattern (though we need to interpret bits, not values) + for (int i = 0; i < 4; i++) + { + if (i % 2 == 0) // Mask has all 1s for even elements + { + Assert.Equal(floatA.GetElement(i), floatResult.GetElement(i)); + } + else // Mask has all 0s for odd elements + { + Assert.Equal(floatB.GetElement(i), floatResult.GetElement(i)); + } + } + + // Test with mixed bit patterns for more complex selection + var partialMask = Vector128.Create(unchecked((int)0x0F0F0F0F), unchecked((int)0xF0F0F0F0), unchecked((int)0xAAAAAAAA), unchecked((int)0x55555555)); + // Use the correct parameter order: left(a), right(b), select(partialMask) + var intResult = PackedSimd.BitwiseSelect(a, b, partialMask); + + // For this mixed mask test, verify the result by manually calculating the expected values + int expectedValue0 = (unchecked((int)0x0F0F0F0F) & 1) | (~unchecked((int)0x0F0F0F0F) & 5); + int expectedValue1 = (unchecked((int)0xF0F0F0F0) & 2) | (~unchecked((int)0xF0F0F0F0) & 6); + int expectedValue2 = (unchecked((int)0xAAAAAAAA) & 3) | (~unchecked((int)0xAAAAAAAA) & 7); + int expectedValue3 = (unchecked((int)0x55555555) & 4) | (~unchecked((int)0x55555555) & 8); + + Assert.Equal(Vector128.Create(expectedValue0, expectedValue1, expectedValue2, expectedValue3), intResult); + + // Test edge cases: all bits from one source + var allOnes = Vector128.Create(unchecked((int)0xFFFFFFFF), unchecked((int)0xFFFFFFFF), unchecked((int)0xFFFFFFFF), unchecked((int)0xFFFFFFFF)); + var allZeros = Vector128.Create(0, 0, 0, 0); + + // All bits from a + // Use the correct parameter order: left(a), right(b), select(allOnes) + var allFromA = PackedSimd.BitwiseSelect(a, b, allOnes); + Assert.Equal(a, allFromA); + + // All bits from b + // Use the correct parameter order: left(a), right(b), select(allZeros) + var allFromB = PackedSimd.BitwiseSelect(a, b, allZeros); + Assert.Equal(b, allFromB); + } [Fact] public unsafe void LoadStoreTest() @@ -98,11 +219,13 @@ public unsafe void LoadStoreTest() var vl = Vector128.LoadUnsafe(ref values[0], (nuint)0); var vl2 = Vector128.LoadUnsafe(ref values[0], (nuint)4); + Assert.Equal(loaded, vl); Assert.Equal(loaded2, vl2); vl = Vector128.Load(ptr); vl2 = Vector128.Load(ptr + 4); + Assert.Equal(loaded, vl); Assert.Equal(loaded2, vl2); @@ -126,37 +249,219 @@ 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(3, extracted); 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 v1 = Vector128.Create((byte)250, 251, 252, 253, 254, 255, 255, 255, 250, 251, 252, 253, 254, 255, 255, 255); + var v2 = Vector128.Create((byte)10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10); var addSat = PackedSimd.AddSaturate(v1, v2); - var subSat = PackedSimd.SubtractSaturate(v1, v2); + var subSat = PackedSimd.SubtractSaturate(v2, v1); // 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); + Assert.Equal(Vector128.Create((byte)255), addSat); + + // Verify expected subtraction results + Assert.Equal(Vector128.Create((byte)0), subSat); + + var v3 = Vector128.Create((ushort)65530, 65531, 65532, 65533, 65534, 65535, 65535, 65535); + var v4 = Vector128.Create((ushort)10, 10, 10, 10, 10, 10, 10, 10); + + var addSatUShort = PackedSimd.AddSaturate(v3, v4); + var subSatUShort = PackedSimd.SubtractSaturate(v4, v3); + + // Verify saturation at 65535 for addition + Assert.Equal(Vector128.Create((ushort)65535), addSatUShort); // 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); + Assert.Equal(Vector128.Create((ushort)0), subSatUShort); + } + + [Fact] + public unsafe void SaturatingArithmeticSignedTest() + { + var v1 = Vector128.Create((sbyte)120, 121, 122, 123, 124, 125, 126, 127, 120, 121, 122, 123, 124, 125, 126, 127); + var v2 = Vector128.Create((sbyte)10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10); + + var addSat = PackedSimd.AddSaturate(v1, v2); + var subSat = PackedSimd.SubtractSaturate(v1, v2); + + // Verify saturation at 127 (max sbyte value) for addition + Assert.Equal(Vector128.Create((sbyte)127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127), addSat); + + // Verify expected subtraction results - should be original values minus 10 + Assert.Equal(Vector128.Create((sbyte)110, 111, 112, 113, 114, 115, 116, 117, 110, 111, 112, 113, 114, 115, 116, 117), subSat); + + // Test negative saturation - when results would be below sbyte.MinValue + var v3 = Vector128.Create((sbyte)-120, -121, -122, -123, -124, -125, -126, -127, -120, -121, -122, -123, -124, -125, -126, -128); + var v4 = Vector128.Create((sbyte)10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10); + + var subSat2 = PackedSimd.SubtractSaturate(v3, v4); + + // Verify saturation at -128 (min sbyte value) for subtraction + Assert.Equal(Vector128.Create((sbyte)-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128), subSat2); + + // Test shorts + var s1 = Vector128.Create((short)32000, 32001, 32002, 32003, 32004, 32005, 32006, 32007); + var s2 = Vector128.Create((short)1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000); + + var shortAddSat = PackedSimd.AddSaturate(s1, s2); + var shortSubSat = PackedSimd.SubtractSaturate(s1, s2); + + // Verify saturation at 32767 (max short value) for addition + Assert.Equal(Vector128.Create((short)32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767), shortAddSat); + + // Verify expected subtraction results - should be original values minus 1000 + Assert.Equal(Vector128.Create((short)31000, 31001, 31002, 31003, 31004, 31005, 31006, 31007), shortSubSat); + + // Test negative saturation + var s3 = Vector128.Create((short)-32000, -32001, -32002, -32003, -32004, -32005, -32006, -32007); + var s4 = Vector128.Create((short)1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000); + + var shortSubSat2 = PackedSimd.SubtractSaturate(s3, s4); + + // Verify saturation at -32768 (min short value) for subtraction + Assert.Equal(Vector128.Create((short)-32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768), shortSubSat2); + } + + [Fact] + public unsafe void SaturatingArithmeticForIntegersTest() + { + // Test for sbyte (saturates at -128 and 127) + var sb1 = Vector128.Create((sbyte)120, (sbyte)120, (sbyte)-120, (sbyte)-120, + (sbyte)120, (sbyte)120, (sbyte)-120, (sbyte)-120, + (sbyte)120, (sbyte)120, (sbyte)-120, (sbyte)-120, + (sbyte)120, (sbyte)120, (sbyte)-120, (sbyte)-120); + var sb2 = Vector128.Create((sbyte)10, (sbyte)20, (sbyte)-10, (sbyte)-20, + (sbyte)10, (sbyte)20, (sbyte)-10, (sbyte)-20, + (sbyte)10, (sbyte)20, (sbyte)-10, (sbyte)-20, + (sbyte)10, (sbyte)20, (sbyte)-10, (sbyte)-20); + + var sbAddSat = PackedSimd.AddSaturate(sb1, sb2); + var sbSubSat = PackedSimd.SubtractSaturate(sb1, sb2); + + Assert.Equal(Vector128.Create((sbyte)127, (sbyte)127, (sbyte)-128, (sbyte)-128, + (sbyte)127, (sbyte)127, (sbyte)-128, (sbyte)-128, + (sbyte)127, (sbyte)127, (sbyte)-128, (sbyte)-128, + (sbyte)127, (sbyte)127, (sbyte)-128, (sbyte)-128), sbAddSat); + Assert.Equal(Vector128.Create((sbyte)110, (sbyte)100, (sbyte)-110, (sbyte)-100, + (sbyte)110, (sbyte)100, (sbyte)-110, (sbyte)-100, + (sbyte)110, (sbyte)100, (sbyte)-110, (sbyte)-100, + (sbyte)110, (sbyte)100, (sbyte)-110, (sbyte)-100), sbSubSat); + + // Test for short (saturates at -32768 and 32767) + var short1 = Vector128.Create((short)32000, (short)32000, (short)-32000, (short)-32000, + (short)30000, (short)30000, (short)-30000, (short)-30000); + var short2 = Vector128.Create((short)1000, (short)2000, (short)-1000, (short)-2000, + (short)1000, (short)2000, (short)-1000, (short)-2000); + + var shortAddSat = PackedSimd.AddSaturate(short1, short2); + var shortSubSat = PackedSimd.SubtractSaturate(short1, short2); + + Assert.Equal(Vector128.Create((short)32767, (short)32767, (short)-32768, (short)-32768, + (short)31000, (short)32000, (short)-31000, (short)-32000), shortAddSat); + Assert.Equal(Vector128.Create((short)31000, (short)30000, (short)-31000, (short)-30000, + (short)29000, (short)28000, (short)-29000, (short)-28000), shortSubSat); + } + + [Fact] + public unsafe void SaturatingArithmeticForUnsignedIntegersTest() + { + // Test for byte (saturates at 0 and 255) + var b1 = Vector128.Create((byte)250, (byte)250, (byte)5, (byte)5, + (byte)250, (byte)250, (byte)5, (byte)5, + (byte)250, (byte)250, (byte)5, (byte)5, + (byte)250, (byte)250, (byte)5, (byte)5); + var b2 = Vector128.Create((byte)10, (byte)20, (byte)10, (byte)20, + (byte)10, (byte)20, (byte)10, (byte)20, + (byte)10, (byte)20, (byte)10, (byte)20, + (byte)10, (byte)20, (byte)10, (byte)20); + + var bAddSat = PackedSimd.AddSaturate(b1, b2); + var bSubSat = PackedSimd.SubtractSaturate(b1, b2); + + Assert.Equal(Vector128.Create((byte)255, (byte)255, (byte)15, (byte)25, + (byte)255, (byte)255, (byte)15, (byte)25, + (byte)255, (byte)255, (byte)15, (byte)25, + (byte)255, (byte)255, (byte)15, (byte)25), bAddSat); + Assert.Equal(Vector128.Create((byte)240, (byte)230, (byte)0, (byte)0, + (byte)240, (byte)230, (byte)0, (byte)0, + (byte)240, (byte)230, (byte)0, (byte)0, + (byte)240, (byte)230, (byte)0, (byte)0), bSubSat); + + // Test for ushort (saturates at 0 and 65535) + var ushort1 = Vector128.Create((ushort)65000, (ushort)65000, (ushort)5, (ushort)5, + (ushort)60000, (ushort)60000, (ushort)10, (ushort)10); + var ushort2 = Vector128.Create((ushort)1000, (ushort)2000, (ushort)10, (ushort)20, + (ushort)10000, (ushort)20000, (ushort)15, (ushort)25); + + var ushortAddSat = PackedSimd.AddSaturate(ushort1, ushort2); + var ushortSubSat = PackedSimd.SubtractSaturate(ushort1, ushort2); + + Assert.Equal(Vector128.Create((ushort)65535, (ushort)65535, (ushort)15, (ushort)25, + (ushort)65535, (ushort)65535, (ushort)25, (ushort)35), ushortAddSat); + Assert.Equal(Vector128.Create((ushort)64000, (ushort)63000, (ushort)0, (ushort)0, + (ushort)50000, (ushort)40000, (ushort)0, (ushort)0), ushortSubSat); + } + + [Fact] + public unsafe void SaturatingArithmeticEdgeCasesTest() + { + // Edge cases for signed bytes + var sbMax = Vector128.Create(sbyte.MaxValue); + var sbMin = Vector128.Create(sbyte.MinValue); + var sbOne = Vector128.Create((sbyte)1); + + var sbOverflow = PackedSimd.AddSaturate(sbMax, sbOne); + var sbUnderflow = PackedSimd.SubtractSaturate(sbMin, sbOne); + + Assert.Equal(Vector128.Create(sbyte.MaxValue), sbOverflow); + Assert.Equal(Vector128.Create(sbyte.MinValue), sbUnderflow); + + // Edge cases for unsigned bytes + var bMax = Vector128.Create(byte.MaxValue); + var bMin = Vector128.Create(byte.MinValue); + var bOne = Vector128.Create((byte)1); + + var bOverflow = PackedSimd.AddSaturate(bMax, bOne); + var bUnderflow = PackedSimd.SubtractSaturate(bMin, bOne); + + Assert.Equal(Vector128.Create(byte.MaxValue), bOverflow); + Assert.Equal(Vector128.Create(byte.MinValue), bUnderflow); + + // Edge cases for signed shorts + var shortMax = Vector128.Create(short.MaxValue); + var shortMin = Vector128.Create(short.MinValue); + var shortOne = Vector128.Create((short)1); + + var shortOverflow = PackedSimd.AddSaturate(shortMax, shortOne); + var shortUnderflow = PackedSimd.SubtractSaturate(shortMin, shortOne); + + Assert.Equal(Vector128.Create(short.MaxValue), shortOverflow); + Assert.Equal(Vector128.Create(short.MinValue), shortUnderflow); + + // Edge cases for unsigned shorts + var ushortMax = Vector128.Create(ushort.MaxValue); + var ushortMin = Vector128.Create(ushort.MinValue); + var ushortOne = Vector128.Create((ushort)1); + + var ushortOverflow = PackedSimd.AddSaturate(ushortMax, ushortOne); + var ushortUnderflow = PackedSimd.SubtractSaturate(ushortMin, ushortOne); + + Assert.Equal(Vector128.Create(ushort.MaxValue), ushortOverflow); + Assert.Equal(Vector128.Create(ushort.MinValue), ushortUnderflow); } [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 v = Vector128.Create((short)1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000); var lowerWidened = PackedSimd.SignExtendWideningLower(v); var upperWidened = PackedSimd.SignExtendWideningUpper(v); @@ -168,15 +473,11 @@ public unsafe void WideningOperationsTest() [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 v = Vector128.Create((byte)1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + var indices = Vector128.Create((byte)3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 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); + Assert.Equal(Vector128.Create((byte)4, 3, 2, 1, 8, 7, 6, 5, 12, 11, 10, 9, 16, 15, 14, 13), swizzled); } [Fact] @@ -202,8 +503,7 @@ public unsafe void LoadWideningTest() 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); + Assert.Equal(Vector128.Create((ushort)1, 2, 3, 4, 5, 6, 7, 8), widened); } } @@ -246,24 +546,17 @@ public unsafe void ConversionTest() [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 bytes = Vector128.Create((byte)1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 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); + Assert.Equal(Vector128.Create((ushort)3, 7, 11, 15, 19, 23, 27, 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 shorts = Vector128.Create((short)10, 20, 30, 40, 50, 60, 70, 80); + var multiplier = Vector128.Create((short)2, 2, 2, 2, 2, 2, 2, 2); var lowerResult = PackedSimd.MultiplyWideningLower(shorts, multiplier); var upperResult = PackedSimd.MultiplyWideningUpper(shorts, multiplier); @@ -275,10 +568,8 @@ public unsafe void MultiplyWideningTest() [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 v1 = Vector128.Create((short)1, 2, 3, 4, 5, 6, 7, 8); + var v2 = Vector128.Create((short)2, 2, 2, 2, 2, 2, 2, 2); var dot = PackedSimd.Dot(v1, v2); @@ -334,65 +625,44 @@ public unsafe void FloatingPointDivisionTest() [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 bytes = Vector128.Create((sbyte)-1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, 16); + var shorts = Vector128.Create((short)-1, 2, -3, 4, 5, 6, -7, 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); + Assert.Equal(Vector128.Create((sbyte)1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), absBytes); + Assert.Equal(Vector128.Create((short)1, 2, 3, 4, 5, 6, 7, 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 bytes1 = Vector128.Create((byte)1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31); + var bytes2 = Vector128.Create((byte)3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 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); + Assert.Equal(Vector128.Create((byte)2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 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 signedBytes = Vector128.Create((sbyte)-1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, 16); + var unsignedBytes = Vector128.Create((byte)255, 2, 253, 4, 251, 6, 249, 8, 247, 10, 245, 12, 243, 14, 241, 16); + var signedMinV128 = Vector128.Min(signedBytes, signedBytes.WithElement(0, (sbyte)0)); + var unsignedMinV128 = Vector128.Min(unsignedBytes, unsignedBytes.WithElement(0, (byte)0)); 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)); + Assert.Equal((sbyte)-1, signedMinV128.GetElement(0)); + Assert.Equal((byte)0, unsignedMinV128.GetElement(0)); + + Assert.Equal(signedMinV128, signedMin); + Assert.Equal(unsignedMinV128, unsignedMin); } [Fact] @@ -404,22 +674,10 @@ public unsafe void SplatTypes() Assert.Equal(Vector128.Create(2.5, 2.5), PackedSimd.Splat(2.5)); Assert.Equal(Vector128.Create(-2L, -2L), PackedSimd.Splat(-2L)); Assert.Equal(Vector128.Create(2UL, 2UL), PackedSimd.Splat(2UL)); - Assert.Equal(Vector128.Create( - (byte)2, (byte)2, (byte)2, (byte)2, - (byte)2, (byte)2, (byte)2, (byte)2, - (byte)2, (byte)2, (byte)2, (byte)2, - (byte)2, (byte)2, (byte)2, (byte)2), PackedSimd.Splat((byte)2)); - Assert.Equal(Vector128.Create( - (sbyte)-2, (sbyte)-2, (sbyte)-2, (sbyte)-2, - (sbyte)-2, (sbyte)-2, (sbyte)-2, (sbyte)-2, - (sbyte)-2, (sbyte)-2, (sbyte)-2, (sbyte)-2, - (sbyte)-2, (sbyte)-2, (sbyte)-2, (sbyte)-2), PackedSimd.Splat((sbyte)-2)); - Assert.Equal(Vector128.Create( - (short)-2, (short)-2, (short)-2, (short)-2, - (short)-2, (short)-2, (short)-2, (short)-2), PackedSimd.Splat((short)-2)); - Assert.Equal(Vector128.Create( - (ushort)2, (ushort)2, (ushort)2, (ushort)2, - (ushort)2, (ushort)2, (ushort)2, (ushort)2), PackedSimd.Splat((ushort)2)); + Assert.Equal(Vector128.Create((byte)2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), PackedSimd.Splat((byte)2)); + Assert.Equal(Vector128.Create((sbyte)-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2), PackedSimd.Splat((sbyte)-2)); + Assert.Equal(Vector128.Create((short)-2, -2, -2, -2, -2, -2, -2, -2), PackedSimd.Splat((short)-2)); + Assert.Equal(Vector128.Create((ushort)2, 2, 2, 2, 2, 2, 2, 2), PackedSimd.Splat((ushort)2)); Assert.Equal(Vector128.Create([(nint)2, (nint)2, (nint)2, (nint)2]), PackedSimd.Splat((nint)2)); Assert.Equal(Vector128.Create([(nuint)2, (nuint)2, (nuint)2, (nuint)2]), PackedSimd.Splat((nuint)2)); } @@ -466,6 +724,14 @@ 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 minResultV128 = Vector128.Min(v1, v2); + var maxResultV128 = Vector128.Max(v1, v2); + + Assert.True(float.IsNaN(minResultV128.GetElement(1))); + Assert.True(float.IsNaN(maxResultV128.GetElement(2))); + Assert.Equal(float.NegativeInfinity, minResultV128.GetElement(0)); + Assert.Equal(float.PositiveInfinity, maxResultV128.GetElement(3)); + var minResult = PackedSimd.Min(v1, v2); var maxResult = PackedSimd.Max(v1, v2); @@ -487,8 +753,8 @@ public unsafe void NativeIntegerArithmeticTest() 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); + Assert.Equal(Vector128.Create([(nint)(-4), (-4), (-4), (-4)]), subResult); + Assert.Equal(Vector128.Create([(nint)5, 12, 21, 32]), mulResult); } [Fact] @@ -500,7 +766,6 @@ public unsafe void NativeUnsignedIntegerArithmeticTest() 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); @@ -550,10 +815,9 @@ public void NativeIntegerShiftTest() 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); + Assert.Equal(Vector128.Create([(nint)64, (-64), 128, (-128)]), leftShift); + Assert.Equal(Vector128.Create([(nint)4, (-4), 8, (-8)]), rightShiftArith); + Assert.Equal(Vector128.Create([(nint)4, 1073741820, 8, 1073741816]), rightShiftLogical); } [Fact] @@ -563,7 +827,6 @@ public void NativeUnsignedIntegerShiftTest() 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); } @@ -576,40 +839,94 @@ public unsafe void ConvertNarrowingSaturateSignedTest() var result = PackedSimd.ConvertNarrowingSaturateSigned(v1, v2); - Assert.Equal(Vector128.Create((short)32767, (short)32767, (short)-32768, (short)-32768, (short)100, (short)200, (short)-100, (short)-200), result); + Assert.Equal(Vector128.Create((short)32767, 32767, -32768, -32768, 100, 200, -100, -200), result); + } + + [Fact] + public unsafe void ConvertNarrowingSaturateUnsignedShortToByte() + { + // Test shorts to bytes - valid values and values that need saturation + var lower = Vector128.Create((short)255, 256, 127, -1, 300, 0, 200, 100); + var upper = Vector128.Create((short)50, 150, -50, -150, 75, 175, 225, 0); + + var result = PackedSimd.ConvertNarrowingSaturateUnsigned(lower, upper); + + // Values should saturate between 0 and 255 + // - Values >= 256 saturate to 255 + // - Negative values saturate to 0 + // - Values between 0-255 remain unchanged + Assert.Equal(Vector128.Create( + (byte)255, 255, 127, 0, 255, 0, 200, 100, + 50, 150, 0, 0, 75, 175, 225, 0 + ), result); + + // Edge cases - test with maximum short values and extreme negative values + var lowerEdge = Vector128.Create((short)32767, 256, 255, 0, -1, -32768, 1, 254); + var upperEdge = Vector128.Create((short)1, 2, 3, 4, 32767, 16384, 8192, 4096); + + var resultEdge = PackedSimd.ConvertNarrowingSaturateUnsigned(lowerEdge, upperEdge); + + // All values above 255 should saturate to 255 + // All negative values should saturate to 0 + Assert.Equal(Vector128.Create( + (byte)255, 255, 255, 0, 0, 0, 1, 254, + 1, 2, 3, 4, 255, 255, 255, 255 + ), resultEdge); } [Fact] - public unsafe void ConvertNarrowingSaturateUnsignedTest() + public unsafe void ConvertNarrowingSaturateUnsignedIntToUShort() { + // Existing test renamed for clarity (was ConvertNarrowingSaturateUnsignedTest) var v1 = Vector128.Create(65535, 65536, -1, -100); var v2 = Vector128.Create(100, 200, 300, 400); var result = PackedSimd.ConvertNarrowingSaturateUnsigned(v1, v2); - Assert.Equal(Vector128.Create((ushort)65535, (ushort)65535, (ushort)0, (ushort)0, (ushort)100, (ushort)200, (ushort)300, (ushort)400), result); + Assert.Equal(Vector128.Create((ushort)65535, 65535, 0, 0, 100, 200, 300, 400), result); + + // Edge cases - test with maximum int values and extreme negative values + var lowerEdge = Vector128.Create(int.MaxValue, 65536, 65535, 0); + var upperEdge = Vector128.Create(-1, int.MinValue, 32768, 32767); + + var resultEdge = PackedSimd.ConvertNarrowingSaturateUnsigned(lowerEdge, upperEdge); + + // Values > 65535 should saturate to 65535 + // Negative values should saturate to 0 + Assert.Equal(Vector128.Create( + (ushort)65535, 65535, 65535, 0, + 0, 0, 32768, 32767 + ), resultEdge); } [Fact] public unsafe void BitmaskTest() { - var v1 = Vector128.Create((byte)0b00000001, (byte)0b00000010, (byte)0b00000100, (byte)0b00001000, - (byte)0b00010000, (byte)0b00100000, (byte)0b01000000, (byte)0b10000000, - (byte)0b00000001, (byte)0b00000010, (byte)0b00000100, (byte)0b00001000, - (byte)0b00010000, (byte)0b10100000, (byte)0b01000000, (byte)0b10000000); - - var v2 = Vector128.Create((ushort)0b1100001001100001, (ushort)0b0000000000000010, (ushort)0b0000000000000100, (ushort)0b0000000000001000, - (ushort)0b0000000000010000, (ushort)0b0000000000100000, (ushort)0b0000000001000000, (ushort)0b0000000010000000); + var v1 = Vector128.Create((byte)0b00000001, 0b00000010, 0b00000100, 0b00001000, + 0b00010000, 0b00100000, 0b01000000, 0b10000000, + 0b00000001, 0b00000010, 0b00000100, 0b00001000, + 0b00010000, 0b10100000, 0b01000000, 0b10000000); + var v2 = Vector128.Create((ushort)0b1100001001100001, 0b0000000000000010, 0b0000000000000100, 0b0000000000001000, + 0b0000000000010000, 0b0000000000100000, 0b0000000001000000, 0b0000000010000000); var v3 = Vector128.Create(0b10000000000000000000000000000001, 0b00000000000111111000000000000010, - 0b00000000000000000000000000000100, 0b10000000000000000000000000001000); - - var bitmask1 = PackedSimd.Bitmask(v1); - var bitmask2 = PackedSimd.Bitmask(v2); - var bitmask3 = PackedSimd.Bitmask(v3); - Assert.Equal(0b1010000010000000, bitmask1); - Assert.Equal(0b1, bitmask2); - Assert.Equal(0b1001, bitmask3); + 0b00000000000000000000000000000100, 0b10000000000000000000000000001000); + + var bitmask_b = PackedSimd.Bitmask(v1); + var bitmask_s = PackedSimd.Bitmask(v2); + var bitmask_i = PackedSimd.Bitmask(v3); + + var v128emsb_b = Vector128.ExtractMostSignificantBits(v1); + var v128emsb_s = Vector128.ExtractMostSignificantBits(v2); + var v128emsb_i = Vector128.ExtractMostSignificantBits(v3); + + Assert.Equal(0b1010000010000000, bitmask_b); + Assert.Equal(0b1, bitmask_s); + Assert.Equal(0b1001, bitmask_i); + + Assert.Equal(v128emsb_b, (uint)bitmask_b); + Assert.Equal(v128emsb_s, (uint)bitmask_s); + Assert.Equal(v128emsb_i, (uint)bitmask_i); } } } \ No newline at end of file diff --git a/src/mono/mono/mini/interp/transform-simd.c b/src/mono/mono/mini/interp/transform-simd.c index e9163bae47ab6a..9f30126531f505 100644 --- a/src/mono/mono/mini/interp/transform-simd.c +++ b/src/mono/mono/mini/interp/transform-simd.c @@ -156,7 +156,6 @@ static guint16 packedsimd_alias_methods [] = { SN_ConvertToSingle, SN_Divide, SN_Equals, - SN_ExtractMostSignificantBits, SN_Floor, SN_GreaterThan, SN_GreaterThanOrEqual, diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index b619feb93c3737..f310f7364f5d88 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -12395,22 +12395,38 @@ MONO_RESTORE_WARNING break; } #endif -#ifdef TARGET_WASM - case OP_WASM_ONESCOMPLEMENT: { - LLVMTypeRef i4v128_t = LLVMVectorType (i4_t, 4); +#if defined(TARGET_ARM64) || defined(TARGET_AMD64) || defined(TARGET_WASM) + case OP_ONES_COMPLEMENT: { LLVMTypeRef ret_t = LLVMTypeOf (lhs); - LLVMValueRef cast = LLVMBuildBitCast (builder, lhs, i4v128_t, ""); - LLVMValueRef result = LLVMBuildNot (builder, cast, "wasm_not"); - values [ins->dreg] = LLVMBuildBitCast (builder, result, ret_t, ""); + LLVMValueRef result = bitcast_to_integral (ctx, lhs); + result = LLVMBuildNot (builder, result, "v128_not"); + result = convert (ctx, result, ret_t); + values [ins->dreg] = result; break; } +#if defined(TARGET_WASM) + case OP_WASM_BITSELECT: // Fall through to OP_BSL: #endif -#if defined(TARGET_ARM64) || defined(TARGET_AMD64) || defined(TARGET_WASM) case OP_BSL: { + LLVMValueRef select; + LLVMValueRef left; + LLVMValueRef right; + + if (ins->opcode == OP_BSL) { + // OP_BSL: Vector128.ConditionalSelect + // (mask, left, right) + select = bitcast_to_integral (ctx, lhs); + left = bitcast_to_integral (ctx, rhs); + right = bitcast_to_integral (ctx, arg3); + } else { + // OP_WASM_BITSELECT: PackedSimd.BitwiseSelect + // (left, right, mask) + select = bitcast_to_integral (ctx, arg3); + left = bitcast_to_integral (ctx, lhs); + right = bitcast_to_integral (ctx, rhs); + } + LLVMTypeRef ret_t = LLVMTypeOf (rhs); - LLVMValueRef select = bitcast_to_integral (ctx, lhs); - LLVMValueRef left = bitcast_to_integral (ctx, rhs); - LLVMValueRef right = bitcast_to_integral (ctx, arg3); LLVMValueRef result1 = LLVMBuildAnd (builder, select, left, "bit_select"); LLVMValueRef result2 = LLVMBuildAnd (builder, LLVMBuildNot (builder, select, ""), right, ""); LLVMValueRef result = LLVMBuildOr (builder, result1, result2, ""); @@ -12488,16 +12504,7 @@ MONO_RESTORE_WARNING break; } #endif -#if defined(TARGET_ARM64) || defined(TARGET_AMD64) - case OP_ONES_COMPLEMENT: { - LLVMTypeRef ret_t = LLVMTypeOf (lhs); - LLVMValueRef result = bitcast_to_integral (ctx, lhs); - result = LLVMBuildNot (builder, result, ""); - result = convert (ctx, result, ret_t); - values [ins->dreg] = result; - break; - } -#endif + case OP_DUMMY_USE: break; diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index 76a1c49c15e20e..fe0f4bcdffb763 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -856,6 +856,7 @@ MINI_OP(OP_EXPAND_R8, "expand_r8", XREG, FREG, NONE) #if defined(TARGET_WASM) MINI_OP(OP_WASM_SIMD_BITMASK, "wasm_bitmask", IREG, XREG, NONE) +MINI_OP3(OP_WASM_BITSELECT, "wasm_bitselect", XREG, XREG, XREG, XREG) MINI_OP3(OP_WASM_SIMD_SHUFFLE, "wasm_shuffle", XREG, XREG, XREG, XREG) MINI_OP(OP_WASM_SIMD_SUM, "wasm_sum", XREG, XREG, NONE) MINI_OP(OP_WASM_SIMD_SWIZZLE, "wasm_swizzle", XREG, XREG, XREG) @@ -1860,16 +1861,8 @@ MINI_OP(OP_SIMD_LOAD_SCALAR_I8, "simd_load_scalar_i8", XREG, IREG, NONE) MINI_OP(OP_SIMD_LOAD_SCALAR_R8, "simd_load_scalar_r8", XREG, IREG, NONE) MINI_OP(OP_SIMD_STORE, "simd_store", NONE, XREG, XREG) -#if defined(TARGET_WASM) -MINI_OP(OP_WASM_ONESCOMPLEMENT, "wasm_onescomplement", XREG, XREG, NONE) -#endif - -#if defined(TARGET_ARM64) || defined(TARGET_AMD64) -MINI_OP(OP_ONES_COMPLEMENT, "ones_complement", XREG, XREG, NONE) - -#endif // TARGET_ARM64 || TARGET_AMD64 - #if defined(TARGET_ARM64) || defined(TARGET_AMD64) || defined(TARGET_WASM) +MINI_OP(OP_ONES_COMPLEMENT, "ones_complement", XREG, XREG, NONE) MINI_OP(OP_CVT_FP_UI, "convert_fp_to_ui", XREG, XREG, NONE) MINI_OP(OP_CVT_FP_SI, "convert_fp_to_si", XREG, XREG, NONE) MINI_OP(OP_CVT_FP_UI_SCALAR, "convert_fp_to_ui_scalar", XREG, XREG, NONE) diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index 9176c76be61a6b..a4f74aecd8004a 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -448,7 +448,7 @@ emit_simd_ins_for_binary_op (MonoCompile *cfg, MonoClass *klass, MonoMethodSigna static MonoInst* emit_simd_ins_for_unary_op (MonoCompile *cfg, MonoClass *klass, MonoMethodSignature *fsig, MonoInst **args, MonoTypeEnum arg_type, int id) { -#if defined(TARGET_ARM64) || defined(TARGET_AMD64) +#if defined(TARGET_ARM64) || defined(TARGET_AMD64) || defined(TARGET_WASM) int op = -1; switch (id){ case SN_Negate: @@ -463,22 +463,6 @@ emit_simd_ins_for_unary_op (MonoCompile *cfg, MonoClass *klass, MonoMethodSignat g_assert_not_reached (); } return emit_simd_ins_for_sig (cfg, klass, op, -1, arg_type, fsig, args); -#elif defined(TARGET_WASM) - int op = -1; - switch (id) - { - case SN_Negate: - case SN_op_UnaryNegation: - op = OP_NEGATION; - break; - case SN_OnesComplement: - case SN_op_OnesComplement: - op = OP_WASM_ONESCOMPLEMENT; - break; - default: - return NULL; - } - return emit_simd_ins_for_sig (cfg, klass, op, -1, arg_type, fsig, args); #else return NULL; #endif @@ -1907,6 +1891,8 @@ static MonoInst* emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { const char *cmethod_name = cmethod->name; + if (fsig->hasthis) + return FALSE; if (strncmp(cmethod_name, "System.Runtime.Intrinsics.ISimdVector