Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vector.As<TFrom, TTo> #47150

Merged
merged 10 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions src/coreclr/jit/simdashwintrinsic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>, 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)
Expand All @@ -405,6 +414,23 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic,
}
#endif // TARGET_X86

#if defined(TARGET_XARCH)
Copy link
Member

@tannergooding tannergooding Feb 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that this is actually under a #if defined(TARGET_XARCH) already (defined on L385) and so this is never hit on arm64.

The simplest fix is to remove the #if defined(TARGET_XARCH) here and then copy the following logic into the #elif defined(TARGET_ARM64) on L446

    switch (intrinsic)
    {
        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;
        }

        default:
        {
            // Most intrinsics have some path that works even if only AdvSimd is available
            break;
        }
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I'll look at the structure and find the opportunity to share code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reordered the ifdef block into two. One for general instruction set availability, one for special logic for each instruction. Is this sure?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks correct. I think this will be good to merge if CI passes

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))
Expand All @@ -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<T>, 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;

Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/simdashwintrinsiclistarm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ SIMD_AS_HWINTRINSIC_ID(Vector4, SquareRoot,
// Vector<T> 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)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/simdashwintrinsiclistxarch.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ SIMD_AS_HWINTRINSIC_ID(Vector4, SquareRoot,
// Vector<T> 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)
Expand Down Expand Up @@ -138,6 +139,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, SquareRoot,
// Vector<T> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ public static partial class Vector
public static System.Numerics.Vector<T> Abs<T>(System.Numerics.Vector<T> value) where T : struct { throw null; }
public static System.Numerics.Vector<T> Add<T>(System.Numerics.Vector<T> left, System.Numerics.Vector<T> right) where T : struct { throw null; }
public static System.Numerics.Vector<T> AndNot<T>(System.Numerics.Vector<T> left, System.Numerics.Vector<T> right) where T : struct { throw null; }
public static System.Numerics.Vector<TTo> As<TFrom, TTo>(this System.Numerics.Vector<TFrom> vector) where TFrom : struct where TTo : struct { throw null; }
public static System.Numerics.Vector<System.Byte> AsVectorByte<T>(System.Numerics.Vector<T> value) where T : struct { throw null; }
public static System.Numerics.Vector<System.Double> AsVectorDouble<T>(System.Numerics.Vector<T> value) where T : struct { throw null; }
public static System.Numerics.Vector<System.Int16> AsVectorInt16<T>(System.Numerics.Vector<T> value) where T : struct { throw null; }
Expand Down
90 changes: 90 additions & 0 deletions src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3047,6 +3047,96 @@ public void NarrowDouble()

#endregion Narrow / Widen

#region Explicit Cast/As
[Fact]
public void TestCastByteToInt() => TestCastToInt<byte>();

[Fact]
public void TestCastSByteToInt() => TestCastToInt<sbyte>();

[Fact]
public void TestCastInt16ToInt() => TestCastToInt<short>();

[Fact]
public void TestCastUInt16ToInt() => TestCastToInt<ushort>();

[Fact]
public void TestCastInt32ToInt() => TestCastToInt<int>();

[Fact]
public void TestCastUInt32ToInt() => TestCastToInt<uint>();

[Fact]
public void TestCastInt64ToInt() => TestCastToInt<long>();

[Fact]
public void TestCastUInt64ToInt() => TestCastToInt<ulong>();

[Fact]
public void TestCastSingleToInt() => TestCastToInt<float>();

[Fact]
public void TestCastDoubleToInt() => TestCastToInt<double>();

private unsafe void TestCastToInt<T>() where T : unmanaged
{
T[] values = GenerateRandomValuesForVector<T>();
Vector<T> vector1 = new Vector<T>(values);
Vector<int> vector2 = (Vector<int>)vector1;

var vector1Bytes = new byte[sizeof(T) * Vector<T>.Count];
vector1.CopyTo(vector1Bytes);
var vector2Bytes = new byte[sizeof(int) * Vector<int>.Count];
vector2.CopyTo(vector2Bytes);

Assert.Equal(vector1Bytes, vector2Bytes);
}

[Fact]
public void TestAsIntToByte() => TestAs<int, byte>();

[Fact]
public void TestAsIntToSByte() => TestAs<int, sbyte>();

[Fact]
public void TestAsIntToInt16() => TestAs<int, short>();

[Fact]
public void TestAsIntToUInt16() => TestAs<int, ushort>();

[Fact]
public void TestAsIntToInt32() => TestAs<int, int>();

[Fact]
public void TestAsIntToUInt32() => TestAs<int, uint>();

[Fact]
public void TestAsIntToInt64() => TestAs<int, long>();

[Fact]
public void TestAsIntToUInt64() => TestAs<int, ulong>();

[Fact]
public void TestAsIntToSingle() => TestAs<int, float>();

[Fact]
public void TestAsIntToDouble() => TestAs<int, double>();

private unsafe void TestAs<TFrom, TTo>() where TFrom : unmanaged where TTo : unmanaged
{
TFrom[] values = GenerateRandomValuesForVector<TFrom>();
Vector<TFrom> vector1 = new Vector<TFrom>(values);
Vector<TTo> vector2 = vector1.As<TFrom, TTo>();

var vector1Bytes = new byte[sizeof(TFrom) * Vector<TFrom>.Count];
vector1.CopyTo(vector1Bytes);
var vector2Bytes = new byte[sizeof(TTo) * Vector<TTo>.Count];
vector2.CopyTo(vector2Bytes);

Assert.Equal(vector1Bytes, vector2Bytes);
}
#endregion

#region Helper Methods
private static void AssertEqual<T>(T expected, T actual, string operation, int precision = -1) where T : IEquatable<T>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,5 +344,19 @@ public void ToVectorDoubleTest()
Vector<bool> vector = default;
Assert.Throws<NotSupportedException>(() => (Vector<double>)vector);
}

[Fact]
public void AsFromTest()
{
Vector<bool> vector = default;
Assert.Throws<NotSupportedException>(() => vector.As<bool, int>());
}

[Fact]
public void AsToTest()
{
Vector<int> vector = default;
Assert.Throws<NotSupportedException>(() => vector.As<int, bool>());
tannergooding marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
23 changes: 23 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Internal.Runtime.CompilerServices;

namespace System.Numerics
{
Expand Down Expand Up @@ -1431,5 +1432,27 @@ internal static void ThrowInsufficientNumberOfElementsException(int requiredElem
{
throw new IndexOutOfRangeException(SR.Format(SR.Arg_InsufficientNumberOfElements, requiredElementCount, "values"));
}

/// <summary>
/// Reinterprets a <see cref="Vector{T}"/> as a <see cref="Vector{T}"/> of new type.
/// </summary>
/// <typeparam name="TFrom">The type of the input vector.</typeparam>
/// <typeparam name="TTo">The type to reinterpret the vector as.</typeparam>
/// <param name="vector">The vector to reinterpret.</param>
/// <returns><paramref name="vector"/> reinterpreted as a new <see cref="Vector{T}"/>.</returns>
/// <exception cref="NotSupportedException">
/// The type of <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> is not supported.
/// </exception>
[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector<TTo> As<TFrom, TTo>(this Vector<TFrom> vector)
where TFrom : struct
where TTo : struct
{
ThrowHelper.ThrowForUnsupportedVectorBaseType<TFrom>();
ThrowHelper.ThrowForUnsupportedVectorBaseType<TTo>();

return Unsafe.As<Vector<TFrom>, Vector<TTo>>(ref vector);
}
}
}