dotnet / dotnet/runtime

[API Proposal]: Mark Vector-related APIs as caller-unsafe

Open
#128,075 4 comments 0 reactions 1 assignee Claimed by @EgorBo View on GitHub
api-approved area-System.Runtime.Intrinsics reduce-unsafe
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Background and motivation

https://github.com/dotnet/runtime/issues/125145 Annotate All Vector-related APIs as caller-unsafe because all of these either:
- Perform memory accesses without bound checks
- May return uninitialized memory in their components.

I'm using a diff format where `+` indicates that it's now caller-unsafe and `-` indicates the unsafe modifier has been removed (a non-breaking change).

### API Proposal

### namespace `System.Runtime.Intrinsics`

```diff
namespace System.Runtime.Intrinsics

public static partial class Vector64
{
+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector64 CreateScalarUnsafe(byte value)
+ unsafe
public static Vector64 CreateScalarUnsafe(double value)
+ unsafe
public static Vector64 CreateScalarUnsafe(short value)
+ unsafe
public static Vector64 CreateScalarUnsafe(int value)
+ unsafe
public static Vector64 CreateScalarUnsafe(long value)
+ unsafe
public static Vector64 CreateScalarUnsafe(nint value)
+ unsafe
public static Vector64 CreateScalarUnsafe(nuint value)
+ unsafe
public static Vector64 CreateScalarUnsafe(sbyte value)
+ unsafe
public static Vector64 CreateScalarUnsafe(float value)
+ unsafe
public static Vector64 CreateScalarUnsafe(ushort value)
+ unsafe
public static Vector64 CreateScalarUnsafe(uint value)
+ unsafe
public static Vector64 CreateScalarUnsafe(ulong value)
+ unsafe
public static Vector64 CreateScalarUnsafe(T value)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector64 LoadUnsafe(ref readonly T source)
+ unsafe
public static Vector64 LoadUnsafe(ref readonly T source, nuint elementOffset)

+ /// Stores a full vector starting at the given reference with no bounds check; may write past the end of the destination.
+ unsafe
public static void StoreUnsafe(this Vector64 source, ref T destination)
+ unsafe
public static void StoreUnsafe(this Vector64 source, ref T destination, nuint elementOffset)

+ /// Widens to a larger vector, leaving the upper elements uninitialized.
+ unsafe
public static Vector128 ToVector128Unsafe(this Vector64 vector)
}

public static partial class Vector128
{
+ /// Reinterprets the value as a 128-bit vector, leaving the upper elements uninitialized.
+ unsafe
public static Vector128 AsVector128Unsafe(this System.Numerics.Vector2 value)
+ unsafe
public static Vector128 AsVector128Unsafe(this System.Numerics.Vector3 value)

+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector128 CreateScalarUnsafe(byte value)
+ unsafe
public static Vector128 CreateScalarUnsafe(double value)
+ unsafe
public static Vector128 CreateScalarUnsafe(short value)
+ unsafe
public static Vector128 CreateScalarUnsafe(int value)
+ unsafe
public static Vector128 CreateScalarUnsafe(long value)
+ unsafe
public static Vector128 CreateScalarUnsafe(nint value)
+ unsafe
public static Vector128 CreateScalarUnsafe(nuint value)
+ unsafe
public static Vector128 CreateScalarUnsafe(sbyte value)
+ unsafe
public static Vector128 CreateScalarUnsafe(float value)
+ unsafe
public static Vector128 CreateScalarUnsafe(ushort value)
+ unsafe
public static Vector128 CreateScalarUnsafe(uint value)
+ unsafe
public static Vector128 CreateScalarUnsafe(ulong value)
+ unsafe
public static Vector128 CreateScalarUnsafe(T value)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector128 LoadUnsafe(ref readonly T source)
+ unsafe
public static Vector128 LoadUnsafe(ref readonly T source, nuint elementOffset)

+ /// Stores a full vector starting at the given reference with no bounds check; may write past the end of the destination.
+ unsafe
public static void StoreUnsafe(this Vector128 source, ref T destination)
+ unsafe
public static void StoreUnsafe(this Vector128 source, ref T destination, nuint elementOffset)

+ /// Widens to a larger vector, leaving the upper elements uninitialized.
+ unsafe
public static Vector256 ToVector256Unsafe(this Vector128 vector)
}

public static partial class Vector256
{
+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector256 CreateScalarUnsafe(byte value)
+ unsafe
public static Vector256 CreateScalarUnsafe(double value)
+ unsafe
public static Vector256 CreateScalarUnsafe(short value)
+ unsafe
public static Vector256 CreateScalarUnsafe(int value)
+ unsafe
public static Vector256 CreateScalarUnsafe(long value)
+ unsafe
public static Vector256 CreateScalarUnsafe(nint value)
+ unsafe
public static Vector256 CreateScalarUnsafe(nuint value)
+ unsafe
public static Vector256 CreateScalarUnsafe(sbyte value)
+ unsafe
public static Vector256 CreateScalarUnsafe(float value)
+ unsafe
public static Vector256 CreateScalarUnsafe(ushort value)
+ unsafe
public static Vector256 CreateScalarUnsafe(uint value)
+ unsafe
public static Vector256 CreateScalarUnsafe(ulong value)
+ unsafe
public static Vector256 CreateScalarUnsafe(T value)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector256 LoadUnsafe(ref readonly T source)
+ unsafe
public static Vector256 LoadUnsafe(ref readonly T source, nuint elementOffset)

+ /// Stores a full vector starting at the given reference with no bounds check; may write past the end of the destination.
+ unsafe
public static void StoreUnsafe(this Vector256 source, ref T destination)
+ unsafe
public static void StoreUnsafe(this Vector256 source, ref T destination, nuint elementOffset)

+ /// Widens to a larger vector, leaving the upper elements uninitialized.
+ unsafe
public static Vector512 ToVector512Unsafe(this Vector256 vector)
}

public static partial class Vector512
{
+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector512 CreateScalarUnsafe(byte value)
+ unsafe
public static Vector512 CreateScalarUnsafe(double value)
+ unsafe
public static Vector512 CreateScalarUnsafe(short value)
+ unsafe
public static Vector512 CreateScalarUnsafe(int value)
+ unsafe
public static Vector512 CreateScalarUnsafe(long value)
+ unsafe
public static Vector512 CreateScalarUnsafe(nint value)
+ unsafe
public static Vector512 CreateScalarUnsafe(nuint value)
+ unsafe
public static Vector512 CreateScalarUnsafe(sbyte value)
+ unsafe
public static Vector512 CreateScalarUnsafe(float value)
+ unsafe
public static Vector512 CreateScalarUnsafe(ushort value)
+ unsafe
public static Vector512 CreateScalarUnsafe(uint value)
+ unsafe
public static Vector512 CreateScalarUnsafe(ulong value)
+ unsafe
public static Vector512 CreateScalarUnsafe(T value)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector512 LoadUnsafe(ref readonly T source)
+ unsafe
public static Vector512 LoadUnsafe(ref readonly T source, nuint elementOffset)

+ /// Stores a full vector starting at the given reference with no bounds check; may write past the end of the destination.
+ unsafe
public static void StoreUnsafe(this Vector512 source, ref T destination)
+ unsafe
public static void StoreUnsafe(this Vector512 source, ref T destination, nuint elementOffset)
}
```

### namespace `System.Numerics`

```diff
namespace System.Numerics

public static partial class Vector
{
+ /// Widens to a larger vector, leaving the upper elements uninitialized.
+ unsafe
public static Vector3 AsVector3Unsafe(this Vector2 value)
+ unsafe
public static Vector4 AsVector4Unsafe(this Vector2 value)
+ unsafe
public static Vector4 AsVector4Unsafe(this Vector3 value)

+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector CreateScalarUnsafe(T value)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector LoadUnsafe(ref readonly T source)
+ unsafe
public static Vector LoadUnsafe(ref readonly T source, nuint elementOffset)

+ /// Stores a full vector starting at the given reference with no bounds check; may write past the end of the destination.
+ unsafe
public static void StoreUnsafe(this Vector source, ref T destination)
+ unsafe
public static void StoreUnsafe(this Vector2 source, ref float destination)
+ unsafe
public static void StoreUnsafe(this Vector3 source, ref float destination)
+ unsafe
public static void StoreUnsafe(this Vector4 source, ref float destination)
+ unsafe
public static void StoreUnsafe(this Vector source, ref T destination, nuint elementOffset)
+ unsafe
public static void StoreUnsafe(this Vector2 source, ref float destination, nuint elementOffset)
+ unsafe
public static void StoreUnsafe(this Vector3 source, ref float destination, nuint elementOffset)
+ unsafe
public static void StoreUnsafe(this Vector4 source, ref float destination, nuint elementOffset)
}

public partial struct Vector2
{
+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector2 CreateScalarUnsafe(float x)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector2 LoadUnsafe(ref readonly float source)
+ unsafe
public static Vector2 LoadUnsafe(ref readonly float source, nuint elementOffset)
}

public partial struct Vector3
{
+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector3 CreateScalarUnsafe(float x)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector3 LoadUnsafe(ref readonly float source)
+ unsafe
public static Vector3 LoadUnsafe(ref readonly float source, nuint elementOffset)
}

public partial struct Vector4
{
+ /// Initializes only the lowest element and leaves the remaining elements uninitialized; reading them observes undefined data.
+ unsafe
public static Vector4 CreateScalarUnsafe(float x)

+ /// Loads a full vector starting at the given reference with no bounds check; may read past the end of the source.
+ unsafe
public static Vector4 LoadUnsafe(ref readonly float source)
+ unsafe
public static Vector4 LoadUnsafe(ref readonly float source, nuint elementOffset)
}
```

### API Usage

All these apis now require an unsafe context to call them.

### Alternative Designs

_No response_

### Risks

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.