static bool IBinaryNumber <int> .IsPow2(int value) => BitOperations.IsPow2(value);
// // IBinaryNumber // /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" /> public static bool IsPow2(uint value) => BitOperations.IsPow2(value);
// // IBinaryNumber // /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" /> public static bool IsPow2(long value) => BitOperations.IsPow2(value);
static bool IBinaryNumber <byte> .IsPow2(byte value) => BitOperations.IsPow2((uint)value);
static bool IBinaryNumber <ushort> .IsPow2(ushort value) => BitOperations.IsPow2((uint)value);
/// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" /> public static bool IsPow2(sbyte value) => BitOperations.IsPow2(value);
// // IBinaryNumber // /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" /> public static bool IsPow2(byte value) => BitOperations.IsPow2((uint)value);
static bool IBinaryNumber <ulong> .IsPow2(ulong value) => BitOperations.IsPow2(value);
static bool IBinaryNumber <sbyte> .IsPow2(sbyte value) => BitOperations.IsPow2(value);
static bool IBinaryNumber <short> .IsPow2(short value) => BitOperations.IsPow2(value);
// // IBinaryNumber // /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" /> public static bool IsPow2(ushort value) => BitOperations.IsPow2((uint)value);
public static void Fill <T>(ref T refData, nuint numElements, T value) { // Early checks to see if it's even possible to vectorize - JIT will turn these checks into consts. // - T cannot contain references (GC can't track references in vectors) // - Vectorization must be hardware-accelerated // - T's size must not exceed the vector's size // - T's size must be a whole power of 2 if (RuntimeHelpers.IsReferenceOrContainsReferences <T>()) { goto CannotVectorize; } if (!Vector.IsHardwareAccelerated) { goto CannotVectorize; } if (Unsafe.SizeOf <T>() > Vector <byte> .Count) { goto CannotVectorize; } if (!BitOperations.IsPow2(Unsafe.SizeOf <T>())) { goto CannotVectorize; } if (numElements >= (uint)(Vector <byte> .Count / Unsafe.SizeOf <T>())) { // We have enough data for at least one vectorized write. T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loops below. Vector <byte> vector; if (Unsafe.SizeOf <T>() == 1) { vector = new Vector <byte>(Unsafe.As <T, byte>(ref tmp)); } else if (Unsafe.SizeOf <T>() == 2) { vector = (Vector <byte>)(new Vector <ushort>(Unsafe.As <T, ushort>(ref tmp))); } else if (Unsafe.SizeOf <T>() == 4) { // special-case float since it's already passed in a SIMD reg vector = (typeof(T) == typeof(float)) ? (Vector <byte>)(new Vector <float>((float)(object)tmp !)) : (Vector <byte>)(new Vector <uint>(Unsafe.As <T, uint>(ref tmp))); } else if (Unsafe.SizeOf <T>() == 8) { // special-case double since it's already passed in a SIMD reg vector = (typeof(T) == typeof(double)) ? (Vector <byte>)(new Vector <double>((double)(object)tmp !)) : (Vector <byte>)(new Vector <ulong>(Unsafe.As <T, ulong>(ref tmp))); } else if (Unsafe.SizeOf <T>() == 16) { Vector128 <byte> vec128 = Unsafe.As <T, Vector128 <byte> >(ref tmp); if (Vector <byte> .Count == 16) { vector = vec128.AsVector(); } else if (Vector <byte> .Count == 32) { vector = Vector256.Create(vec128, vec128).AsVector(); } else { Debug.Fail("Vector<T> isn't 128 or 256 bits in size?"); goto CannotVectorize; } } else if (Unsafe.SizeOf <T>() == 32) { if (Vector <byte> .Count == 32) { vector = Unsafe.As <T, Vector256 <byte> >(ref tmp).AsVector(); } else { Debug.Fail("Vector<T> isn't 256 bits in size?"); goto CannotVectorize; } } else { Debug.Fail("Vector<T> is greater than 256 bits in size?"); goto CannotVectorize; } ref byte refDataAsBytes = ref Unsafe.As <T, byte>(ref refData); nuint totalByteLength = numElements * (nuint)Unsafe.SizeOf <T>(); // get this calculation ready ahead of time nuint stopLoopAtOffset = totalByteLength & (nuint)(nint)(2 * (int)-Vector <byte> .Count); // intentional sign extension carries the negative bit nuint offset = 0; // Loop, writing 2 vectors at a time. // Compare 'numElements' rather than 'stopLoopAtOffset' because we don't want a dependency // on the very recently calculated 'stopLoopAtOffset' value. if (numElements >= (uint)(2 * Vector <byte> .Count / Unsafe.SizeOf <T>())) { do { Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset), vector); Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset + (nuint)Vector <byte> .Count), vector); offset += (uint)(2 * Vector <byte> .Count); } while (offset < stopLoopAtOffset); } // At this point, if any data remains to be written, it's strictly less than // 2 * sizeof(Vector) bytes. The loop above had us write an even number of vectors. // If the total byte length instead involves us writing an odd number of vectors, write // one additional vector now. The bit check below tells us if we're in an "odd vector // count" situation. if ((totalByteLength & (nuint)Vector <byte> .Count) != 0) { Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset), vector); } // It's possible that some small buffer remains to be populated - something that won't // fit an entire vector's worth of data. Instead of falling back to a loop, we'll write // a vector at the very end of the buffer. This may involve overwriting previously // populated data, which is fine since we're splatting the same value for all entries. // There's no need to perform a length check here because we already performed this // check before entering the vectorized code path. Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, totalByteLength - (nuint)Vector <byte> .Count), vector); // And we're done! return; }