File: System\IO\Hashing\VectorHelper.cs
Web Access
Project: src\src\libraries\System.IO.Hashing\src\System.IO.Hashing.csproj (System.IO.Hashing)
// 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.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using Aes = System.Runtime.Intrinsics.Arm.Aes;
 
namespace System.IO.Hashing
{
    // Helpers which provide equivalent intrinsics for Intel and ARM architectures. Should only be used
    // if the intrinsics are available.
    internal static class VectorHelper
    {
        // Pclmulqdq implies support for SSE2
        public static bool IsSupported => Pclmulqdq.IsSupported || (Aes.IsSupported && AdvSimd.IsSupported);
 
        // Performs carryless multiplication of the upper pairs of source and constants and the lower pairs of source and constants,
        // then folds them into target using carryless addition.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector128<ulong> FoldPolynomialPair(Vector128<ulong> target, Vector128<ulong> source, Vector128<ulong> constants)
        {
            target ^= CarrylessMultiplyUpper(source, constants);
            target ^= CarrylessMultiplyLower(source, constants);
 
            return target;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector128<ulong> CarrylessMultiplyLower(Vector128<ulong> left, Vector128<ulong> right)
        {
            if (Pclmulqdq.IsSupported)
            {
                return Pclmulqdq.CarrylessMultiply(left, right, 0x00);
            }
 
            if (Aes.IsSupported)
            {
                return Aes.PolynomialMultiplyWideningLower(left.GetLower(), right.GetLower());
            }
 
            ThrowHelper.ThrowUnreachableException();
            return default;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector128<ulong> CarrylessMultiplyUpper(Vector128<ulong> left, Vector128<ulong> right)
        {
            if (Pclmulqdq.IsSupported)
            {
                return Pclmulqdq.CarrylessMultiply(left, right, 0x11);
            }
 
            if (Aes.IsSupported)
            {
                return Aes.PolynomialMultiplyWideningUpper(left, right);
            }
 
            ThrowHelper.ThrowUnreachableException();
            return default;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector128<ulong> CarrylessMultiplyLeftUpperRightLower(Vector128<ulong> left, Vector128<ulong> right)
        {
            if (Pclmulqdq.IsSupported)
            {
                return Pclmulqdq.CarrylessMultiply(left, right, 0x01);
            }
 
            if (Aes.IsSupported)
            {
                return Aes.PolynomialMultiplyWideningLower(left.GetUpper(), right.GetLower());
            }
 
            ThrowHelper.ThrowUnreachableException();
            return default;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector128<ulong> CarrylessMultiplyLeftLowerRightUpper(Vector128<ulong> left, Vector128<ulong> right)
        {
            if (Pclmulqdq.IsSupported)
            {
                return Pclmulqdq.CarrylessMultiply(left, right, 0x10);
            }
 
            if (Aes.IsSupported)
            {
                return Aes.PolynomialMultiplyWideningLower(left.GetLower(), right.GetUpper());
            }
 
            ThrowHelper.ThrowUnreachableException();
            return default;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector128<ulong> ShiftRightBytesInVector(Vector128<ulong> operand,
            [ConstantExpected(Max = (byte)15)] byte numBytesToShift)
        {
            if (Sse2.IsSupported)
            {
                return Sse2.ShiftRightLogical128BitLane(operand, numBytesToShift);
            }
 
            if (AdvSimd.IsSupported)
            {
                return AdvSimd.ExtractVector128(operand.AsByte(), Vector128<byte>.Zero, numBytesToShift).AsUInt64();
            }
 
            ThrowHelper.ThrowUnreachableException();
            return default;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector128<ulong> ShiftLowerToUpper(Vector128<ulong> operand)
        {
            if (Sse2.IsSupported)
            {
                return Sse2.ShiftLeftLogical128BitLane(operand, 8);
            }
 
            if (AdvSimd.IsSupported)
            {
                return AdvSimd.ExtractVector128(Vector128<byte>.Zero, operand.AsByte(), 8).AsUInt64();
            }
 
            ThrowHelper.ThrowUnreachableException();
            return default;
        }
    }
}