diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp index f4f9ec906c783a..d50def6863f45d 100644 --- a/src/coreclr/jit/simdashwintrinsic.cpp +++ b/src/coreclr/jit/simdashwintrinsic.cpp @@ -388,6 +388,15 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, // We should have already exited early if SSE2 isn't supported assert(compIsaSupportedDebugOnly(InstructionSet_SSE2)); + // Vector, when 32-bytes, requires at least AVX2 + assert(!isVectorT256 || compIsaSupportedDebugOnly(InstructionSet_AVX2)); +#elif defined(TARGET_ARM64) + // We should have already exited early if AdvSimd isn't supported + assert(compIsaSupportedDebugOnly(InstructionSet_AdvSimd)); +#else +#error Unsupported platform +#endif // !TARGET_XARCH && !TARGET_ARM64 + switch (intrinsic) { #if defined(TARGET_X86) @@ -405,6 +414,23 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, } #endif // TARGET_X86 +#if defined(TARGET_XARCH) + case NI_VectorT256_As: +#endif // TARGET_XARCH + case NI_VectorT128_As: + { + unsigned retSimdSize; + var_types retBaseType = getBaseTypeAndSizeOfSIMDType(sig->retTypeSigClass, &retSimdSize); + + if (!varTypeIsArithmetic(retBaseType) || (retSimdSize == 0)) + { + // We get here if the return type is an unsupported type + return nullptr; + } + break; + } + +#if defined(TARGET_XARCH) case NI_VectorT128_Dot: { if (!compOpportunisticallyDependsOn(InstructionSet_SSE41)) @@ -417,23 +443,15 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, } break; } +#endif // TARGET_XARCH default: { - // Most intrinsics have some path that works even if only SSE2 is available + // Most intrinsics have some path that works even if only SSE2/AdvSimd is available break; } } - // Vector, when 32-bytes, requires at least AVX2 - assert(!isVectorT256 || compIsaSupportedDebugOnly(InstructionSet_AVX2)); -#elif defined(TARGET_ARM64) - // We should have already exited early if AdvSimd isn't supported - assert(compIsaSupportedDebugOnly(InstructionSet_AdvSimd)); -#else -#error Unsupported platform -#endif // !TARGET_XARCH && !TARGET_ARM64 - GenTree* copyBlkDst = nullptr; GenTree* copyBlkSrc = nullptr; @@ -563,10 +581,10 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, { assert(newobjThis == nullptr); - bool isOpExplicit = (intrinsic == NI_VectorT128_op_Explicit); + bool isOpExplicit = (intrinsic == NI_VectorT128_op_Explicit) || (intrinsic == NI_VectorT128_As); #if defined(TARGET_XARCH) - isOpExplicit |= (intrinsic == NI_VectorT256_op_Explicit); + isOpExplicit |= (intrinsic == NI_VectorT256_op_Explicit) || (intrinsic == NI_VectorT256_As); #endif if (isOpExplicit) diff --git a/src/coreclr/jit/simdashwintrinsiclistarm64.h b/src/coreclr/jit/simdashwintrinsiclistarm64.h index 481e139c2ac5b1..494377bc7172fe 100644 --- a/src/coreclr/jit/simdashwintrinsiclistarm64.h +++ b/src/coreclr/jit/simdashwintrinsiclistarm64.h @@ -102,6 +102,7 @@ SIMD_AS_HWINTRINSIC_ID(Vector4, SquareRoot, // Vector Intrinsics SIMD_AS_HWINTRINSIC_ID(VectorT128, Abs, 1, {NI_AdvSimd_Abs, NI_VectorT128_Abs, NI_AdvSimd_Abs, NI_VectorT128_Abs, NI_AdvSimd_Abs, NI_VectorT128_Abs, NI_AdvSimd_Arm64_Abs, NI_VectorT128_Abs, NI_AdvSimd_Abs, NI_AdvSimd_Arm64_Abs}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, AndNot, 2, {NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear, NI_AdvSimd_BitwiseClear}, SimdAsHWIntrinsicFlag::None) +SIMD_AS_HWINTRINSIC_ID(VectorT128, As, 1, {NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, Ceiling, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Ceiling, NI_AdvSimd_Arm64_Ceiling}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, ConditionalSelect, 3, {NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(VectorT128, CreateBroadcast, ".ctor", 2, {NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod) diff --git a/src/coreclr/jit/simdashwintrinsiclistxarch.h b/src/coreclr/jit/simdashwintrinsiclistxarch.h index d542dc63d31611..65584c3abb09e0 100644 --- a/src/coreclr/jit/simdashwintrinsiclistxarch.h +++ b/src/coreclr/jit/simdashwintrinsiclistxarch.h @@ -102,6 +102,7 @@ SIMD_AS_HWINTRINSIC_ID(Vector4, SquareRoot, // Vector Intrinsics SIMD_AS_HWINTRINSIC_ID(VectorT128, Abs, 1, {NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs, NI_VectorT128_Abs}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, AndNot, 2, {NI_SSE2_AndNot, NI_SSE2_AndNot, NI_SSE2_AndNot, NI_SSE2_AndNot, NI_SSE2_AndNot, NI_SSE2_AndNot, NI_SSE2_AndNot, NI_SSE2_AndNot, NI_SSE_AndNot, NI_SSE2_AndNot}, SimdAsHWIntrinsicFlag::NeedsOperandsSwapped) +SIMD_AS_HWINTRINSIC_ID(VectorT128, As, 1, {NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As, NI_VectorT128_As}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, Ceiling, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE41_Ceiling, NI_SSE41_Ceiling}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, ConditionalSelect, 3, {NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect, NI_VectorT128_ConditionalSelect}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(VectorT128, CreateBroadcast, ".ctor", 2, {NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod) @@ -138,6 +139,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, SquareRoot, // Vector Intrinsics SIMD_AS_HWINTRINSIC_ID(VectorT256, Abs, 1, {NI_AVX2_Abs, NI_VectorT256_Abs, NI_AVX2_Abs, NI_VectorT256_Abs, NI_AVX2_Abs, NI_VectorT256_Abs, NI_VectorT256_Abs, NI_VectorT256_Abs, NI_VectorT256_Abs, NI_VectorT256_Abs}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT256, AndNot, 2, {NI_AVX2_AndNot, NI_AVX2_AndNot, NI_AVX2_AndNot, NI_AVX2_AndNot, NI_AVX2_AndNot, NI_AVX2_AndNot, NI_AVX2_AndNot, NI_AVX2_AndNot, NI_AVX_AndNot, NI_AVX_AndNot}, SimdAsHWIntrinsicFlag::NeedsOperandsSwapped) +SIMD_AS_HWINTRINSIC_ID(VectorT256, As, 1, {NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As, NI_VectorT256_As}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT256, Ceiling, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AVX_Ceiling, NI_AVX_Ceiling}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT256, ConditionalSelect, 3, {NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect, NI_VectorT256_ConditionalSelect}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(VectorT256, CreateBroadcast, ".ctor", 2, {NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod) diff --git a/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs b/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs index 7e293b8fce2331..7ee9ee3c623aaa 100644 --- a/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs +++ b/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs @@ -192,6 +192,7 @@ public static partial class Vector public static System.Numerics.Vector Abs(System.Numerics.Vector value) where T : struct { throw null; } public static System.Numerics.Vector Add(System.Numerics.Vector left, System.Numerics.Vector right) where T : struct { throw null; } public static System.Numerics.Vector AndNot(System.Numerics.Vector left, System.Numerics.Vector right) where T : struct { throw null; } + public static System.Numerics.Vector As(this System.Numerics.Vector vector) where TFrom : struct where TTo : struct { throw null; } public static System.Numerics.Vector AsVectorByte(System.Numerics.Vector value) where T : struct { throw null; } public static System.Numerics.Vector AsVectorDouble(System.Numerics.Vector value) where T : struct { throw null; } public static System.Numerics.Vector AsVectorInt16(System.Numerics.Vector value) where T : struct { throw null; } diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index 0d510ae80b9f51..7031a7341f4537 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs @@ -3047,6 +3047,96 @@ public void NarrowDouble() #endregion Narrow / Widen + #region Explicit Cast/As + [Fact] + public void TestCastByteToInt() => TestCastToInt(); + + [Fact] + public void TestCastSByteToInt() => TestCastToInt(); + + [Fact] + public void TestCastInt16ToInt() => TestCastToInt(); + + [Fact] + public void TestCastUInt16ToInt() => TestCastToInt(); + + [Fact] + public void TestCastInt32ToInt() => TestCastToInt(); + + [Fact] + public void TestCastUInt32ToInt() => TestCastToInt(); + + [Fact] + public void TestCastInt64ToInt() => TestCastToInt(); + + [Fact] + public void TestCastUInt64ToInt() => TestCastToInt(); + + [Fact] + public void TestCastSingleToInt() => TestCastToInt(); + + [Fact] + public void TestCastDoubleToInt() => TestCastToInt(); + + private unsafe void TestCastToInt() where T : unmanaged + { + T[] values = GenerateRandomValuesForVector(); + Vector vector1 = new Vector(values); + Vector vector2 = (Vector)vector1; + + var vector1Bytes = new byte[sizeof(T) * Vector.Count]; + vector1.CopyTo(vector1Bytes); + var vector2Bytes = new byte[sizeof(int) * Vector.Count]; + vector2.CopyTo(vector2Bytes); + + Assert.Equal(vector1Bytes, vector2Bytes); + } + + [Fact] + public void TestAsIntToByte() => TestAs(); + + [Fact] + public void TestAsIntToSByte() => TestAs(); + + [Fact] + public void TestAsIntToInt16() => TestAs(); + + [Fact] + public void TestAsIntToUInt16() => TestAs(); + + [Fact] + public void TestAsIntToInt32() => TestAs(); + + [Fact] + public void TestAsIntToUInt32() => TestAs(); + + [Fact] + public void TestAsIntToInt64() => TestAs(); + + [Fact] + public void TestAsIntToUInt64() => TestAs(); + + [Fact] + public void TestAsIntToSingle() => TestAs(); + + [Fact] + public void TestAsIntToDouble() => TestAs(); + + private unsafe void TestAs() where TFrom : unmanaged where TTo : unmanaged + { + TFrom[] values = GenerateRandomValuesForVector(); + Vector vector1 = new Vector(values); + Vector vector2 = vector1.As(); + + var vector1Bytes = new byte[sizeof(TFrom) * Vector.Count]; + vector1.CopyTo(vector1Bytes); + var vector2Bytes = new byte[sizeof(TTo) * Vector.Count]; + vector2.CopyTo(vector2Bytes); + + Assert.Equal(vector1Bytes, vector2Bytes); + } + #endregion + #region Helper Methods private static void AssertEqual(T expected, T actual, string operation, int precision = -1) where T : IEquatable { diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorUnsupportedTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorUnsupportedTests.cs index 07448360d46498..6d435a630cc5a3 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorUnsupportedTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorUnsupportedTests.cs @@ -344,5 +344,19 @@ public void ToVectorDoubleTest() Vector vector = default; Assert.Throws(() => (Vector)vector); } + + [Fact] + public void AsFromTest() + { + Vector vector = default; + Assert.Throws(() => vector.As()); + } + + [Fact] + public void AsToTest() + { + Vector vector = default; + Assert.Throws(() => vector.As()); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs index 99fff3d99aed89..4943bf57216da3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; namespace System.Numerics { @@ -1431,5 +1432,27 @@ internal static void ThrowInsufficientNumberOfElementsException(int requiredElem { throw new IndexOutOfRangeException(SR.Format(SR.Arg_InsufficientNumberOfElements, requiredElementCount, "values")); } + + /// + /// Reinterprets a as a of new type. + /// + /// The type of the input vector. + /// The type to reinterpret the vector as. + /// The vector to reinterpret. + /// reinterpreted as a new . + /// + /// The type of or is not supported. + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector As(this Vector vector) + where TFrom : struct + where TTo : struct + { + ThrowHelper.ThrowForUnsupportedVectorBaseType(); + ThrowHelper.ThrowForUnsupportedVectorBaseType(); + + return Unsafe.As, Vector>(ref vector); + } } }