Rationale
Code working with strings or string like data is frequently vectorized for performance. However, today this requires explicit checks and unsafe code to produce a Vector128<ushort> from what is typically a ReadOnlySpan<char> based input.
Given that char is part of the generic math feature (implements IBinaryInteger<char>) and has always supported the full set of arithmetic operators in C# (i.e. someChar /= someChar works), it is proposed we just relax the restriction and allow Vector128<char>.IsSupported to report true.
Doing this is mostly just relaxing an import restriction in the JIT.
API Proposal
We can get away with as few as 1 API per type (AsChar). This allows all generic operations to work and only minimally excludes a handful of additional APIs which cannot be generic due to special considerations (such as changing type as part of their operation or for specific arguments). Users could then still access this functionality via AsChar() and AsUInt16().
public static partial class Vector128
{
public static Vector128<char> AsChar<T>(this Vector128<T> vector);
}
// Repeat for Vector64, Vector256, Vector512, and Vector
However, doing so is a bit "unusual" and so for clarity the other APIs to fully match other T would be:
public static partial class Vector128
{
// A user doing `Create('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h')` actually already binds to `Vector128<ushort>` today
public static Vector128<char> Create(char e0, char e1, char e2, char e3, char e4, char e5, char e6, char e7);
// We have `M<T>(...)` variants, the non-generic versions are older and so these are "consistency"
public static Vector128<char> Create(char value);
public static Vector128<char> Create(Vector64<char> lower, Vector64<char> upper);
public static Vector128<char> CreateScalar(char value);
public static Vector128<char> CreateScalarUnsafe(char value);
// Can be achieved with `AsUInt16()`
public static Vector128<byte> Narrow(Vector128<char> lower, Vector128<char> upper);
public static Vector128<byte> NarrowWithSaturation(Vector128<char> lower, Vector128<char> upper);
// Operators exist, these are just named variants
public static Vector128<char> ShiftLeft(Vector128<char> vector, int shiftCount);
public static Vector128<char> ShiftRightLogical(Vector128<char> vector, int shiftCount);
// Indices need to explicitly be integers (same for `float`/`double`)
public static Vectore128<char> Shuffle(Vector128<char> vector, Vector128<ushort> indices);
public static Vectore128<char> ShuffleNative(Vector128<char> vector, Vector128<ushort> indices);
// Can be achieved with `AsUInt16()`, notably no convention for `byte->char` exists so `WidenToChar` or something may also be warranted
public static (Vector128<uint> Lower, Vector128<uint> Upper) Widen(Vector128<char> vector);
public static Vector128<uint> WidenLower(Vector128<char> vector);
public static Vector128<uint> WidenUpper(Vector128<char> vector);
}
// Repeat for Vector64, Vector256, Vector512, and Vector
Alternative Proposal
We could instead say that Vector128<char> is unsupported and only expose APIs for loading/storing as char and have the vector it produces be ushort. We already have some such helpers internally. Much as before, we can also get away with fewer APIs if we want some cases to do something different.
public static partial class Vector128
{
public static void CopyTo<T>(this System.Runtime.Intrinsics.Vector128<ushort> vector, Span<char> destination);
public static System.Runtime.Intrinsics.Vector128<ushort> Create(ReadOnlySpan<char> values);
}
// Repeat for Vector64, Vector256, Vector512, and Vector
However, the set of APIs to match other T would be:
public static partial class Vector128
{
// Can just use the Span overload
public static void CopyTo<T>(this Vector128<T> vector, char[] destination);
public static void CopyTo<T>(this Vector128<T> vector, char[] destination, int startIndex);
public static Vector128<ushort> Create(char[] values);
public static Vector128<ushort> Create(char[] values, int index);
// Casting the pointer is trivial, the API is `caller unsafe`
public static unsafe Vector128<ushort> Load(char* source);
public static unsafe Vector128<ushort> LoadAligned(char* source);
public static unsafe Vector128<ushort> LoadAlignedNonTemporal(char* source);
public static unsafe void Store(this Vector128<ushort> source, char* destination);
public static unsafe void StoreAligned(this Vector128<ushort> source, char* destination);
public static unsafe void StoreAlignedNonTemporal(this Vector128<ushort> source, char* destination);
// Can use `Unsafe.AsRef`, the API will be `caller unsafe`
public static unsafe Vector128<ushort> LoadUnsafe(ref readonly char source);
public static unsafe Vector128<ushort> LoadUnsafe(ref readonly char source, nuint elementOffset);
public static unsafe void StoreUnsafe(this Vector128<ushort> source, ref char destination);
public static unsafe void StoreUnsafe(this Vector128<ushort> source, ref char destination, nuint elementOffset);
}
// Repeat for Vector64, Vector256, Vector512, and Vector
Rationale
Code working with strings or string like data is frequently vectorized for performance. However, today this requires explicit checks and unsafe code to produce a
Vector128<ushort>from what is typically aReadOnlySpan<char>based input.Given that
charis part of the generic math feature (implementsIBinaryInteger<char>) and has always supported the full set of arithmetic operators in C# (i.e.someChar /= someCharworks), it is proposed we just relax the restriction and allowVector128<char>.IsSupportedto reporttrue.Doing this is mostly just relaxing an import restriction in the JIT.
API Proposal
We can get away with as few as 1 API per type (
AsChar). This allows all generic operations to work and only minimally excludes a handful of additional APIs which cannot be generic due to special considerations (such as changing type as part of their operation or for specific arguments). Users could then still access this functionality viaAsChar()andAsUInt16().However, doing so is a bit "unusual" and so for clarity the other APIs to fully match other
Twould be:Alternative Proposal
We could instead say that
Vector128<char>is unsupported and only expose APIs for loading/storing ascharand have the vector it produces beushort. We already have some such helpers internally. Much as before, we can also get away with fewer APIs if we want some cases to do something different.However, the set of APIs to match other
Twould be: