From 698276c7c772d016bfddcac474e6d68f1949c4ba Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 14 Mar 2024 11:58:33 -0400 Subject: [PATCH 1/3] Use MemoryMarshal.Cast in a few places --- .../Tensors/TensorPrimitives.Helpers.cs | 18 +++++++++++++- .../Common/TensorPrimitives.IUnaryOperator.cs | 7 ------ .../TensorPrimitives.ConvertHelpers.cs | 7 ------ .../Tensors/netcore/TensorPrimitives.Round.cs | 4 ++-- .../src/System/Number.Parsing.cs | 24 ++++++++----------- .../Runtime/InteropServices/MemoryMarshal.cs | 6 +++++ 6 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs index 4b5f40cec39d0..4f6edad6786a3 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + namespace System.Numerics.Tensors { /// Performs primitive tensor operations over spans of memory. @@ -23,7 +25,21 @@ private static void ValidateInputOutputSpanNonOverlapping(ReadOnlySpan inp } /// Throws an for trying to negate the minimum value of a two-complement value. - internal static void ThrowNegateTwosCompOverflow() => throw new OverflowException(SR.Overflow_NegateTwosCompNum); + private static void ThrowNegateTwosCompOverflow() => throw new OverflowException(SR.Overflow_NegateTwosCompNum); + + /// Creates a span of from a when they're the same type. + private static unsafe Span Rename(Span span) // MemoryMarshal.Cast, but without constraints or validation + { + Debug.Assert(sizeof(TFrom) == sizeof(TTo)); + return MemoryMarshal.CreateSpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); + } + + /// Creates a span of from a when they're the same type. + private static unsafe ReadOnlySpan Rename(ReadOnlySpan span) // MemoryMarshal.Cast, but without constraints or validation + { + Debug.Assert(sizeof(TFrom) == sizeof(TTo)); + return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); + } /// Mask used to handle alignment elements before vectorized handling of the input. /// diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Common/TensorPrimitives.IUnaryOperator.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Common/TensorPrimitives.IUnaryOperator.cs index 20313a27eb77c..c2c22950c5774 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Common/TensorPrimitives.IUnaryOperator.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Common/TensorPrimitives.IUnaryOperator.cs @@ -1247,12 +1247,5 @@ static void VectorizedSmall8(ref TInput xRef, ref TOutput dRef, nuint remainder) } } } - - /// Creates a span of from a when they're the same type. - private static unsafe Span Rename(Span span) - { - Debug.Assert(sizeof(TFrom) == sizeof(TTo)); - return MemoryMarshal.CreateSpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.ConvertHelpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.ConvertHelpers.cs index ca785bf257aa6..eea5c5ed3a236 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.ConvertHelpers.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.ConvertHelpers.cs @@ -643,13 +643,6 @@ static Vector512 SingleToHalfAsWidenedUInt32(Vector512 value) } } - /// Creates a span of from a when they're the same type. - private static unsafe ReadOnlySpan Rename(ReadOnlySpan span) - { - Debug.Assert(sizeof(TFrom) == sizeof(TTo)); - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - } - /// Gets whether is or if in a 32-bit process. private static bool IsUInt32Like() => typeof(T) == typeof(uint) || (IntPtr.Size == 4 && typeof(T) == typeof(nuint)); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs index 575ce0f523278..83cd73d184435 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs @@ -106,13 +106,13 @@ public static void Round(ReadOnlySpan x, int digits, MidpointRounding mode if (typeof(T) == typeof(float)) { ReadOnlySpan roundPower10Single = [1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f]; - roundPower10 = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(roundPower10Single)), roundPower10Single.Length); + roundPower10 = Rename(roundPower10Single); } else if (typeof(T) == typeof(double)) { Debug.Assert(typeof(T) == typeof(double)); ReadOnlySpan roundPower10Double = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15]; - roundPower10 = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(roundPower10Double)), roundPower10Double.Length); + roundPower10 = Rename(roundPower10Double); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 935328a21d20e..852b979492c2d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -883,16 +883,16 @@ internal static bool SpanStartsWith(ReadOnlySpan span, ReadOnlySpa { if (typeof(TChar) == typeof(char)) { - ReadOnlySpan typedSpan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - ReadOnlySpan typedValue = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); + ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); + ReadOnlySpan typedValue = MemoryMarshal.Cast(value); return typedSpan.StartsWith(typedValue, comparisonType); } else { Debug.Assert(typeof(TChar) == typeof(byte)); - ReadOnlySpan typedSpan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - ReadOnlySpan typedValue = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); + ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); + ReadOnlySpan typedValue = MemoryMarshal.Cast(value); return typedSpan.StartsWithUtf8(typedValue, comparisonType); } } @@ -903,17 +903,13 @@ internal static ReadOnlySpan SpanTrim(ReadOnlySpan span) { if (typeof(TChar) == typeof(char)) { - ReadOnlySpan typedSpan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - ReadOnlySpan result = typedSpan.Trim(); - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(result)), result.Length); + return MemoryMarshal.Cast(MemoryMarshal.Cast(span).Trim()); } else { Debug.Assert(typeof(TChar) == typeof(byte)); - ReadOnlySpan typedSpan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - ReadOnlySpan result = typedSpan.TrimUtf8(); - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(result)), result.Length); + return MemoryMarshal.Cast(MemoryMarshal.Cast(span).TrimUtf8()); } } @@ -923,16 +919,16 @@ internal static bool SpanEqualsOrdinalIgnoreCase(ReadOnlySpan span { if (typeof(TChar) == typeof(char)) { - ReadOnlySpan typedSpan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - ReadOnlySpan typedValue = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); + ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); + ReadOnlySpan typedValue = MemoryMarshal.Cast(value); return typedSpan.EqualsOrdinalIgnoreCase(typedValue); } else { Debug.Assert(typeof(TChar) == typeof(byte)); - ReadOnlySpan typedSpan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); - ReadOnlySpan typedValue = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); + ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); + ReadOnlySpan typedValue = MemoryMarshal.Cast(value); return typedSpan.EqualsOrdinalIgnoreCaseUtf8(typedValue); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs index 0b0da448e4eba..8b04c064383d8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs @@ -122,6 +122,9 @@ public static Span Cast(Span span) if (RuntimeHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + if (typeof(TFrom) == typeof(TTo)) + return new Span(ref Unsafe.As(ref span._reference), span.Length); + // Use unsigned integers - unsigned division by constant (especially by power of 2) // and checked casts are faster and smaller. uint fromSize = (uint)Unsafe.SizeOf(); @@ -177,6 +180,9 @@ public static ReadOnlySpan Cast(ReadOnlySpan span) if (RuntimeHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + if (typeof(TFrom) == typeof(TTo)) + return new ReadOnlySpan(ref Unsafe.As(ref span._reference), span.Length); + // Use unsigned integers - unsigned division by constant (especially by power of 2) // and checked casts are faster and smaller. uint fromSize = (uint)Unsafe.SizeOf(); From 378acaea339fdf700d3ef2113001e54ab689aa7c Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 15 Mar 2024 16:03:53 -0400 Subject: [PATCH 2/3] Removed special-casing --- .../src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs | 4 ++-- .../src/System/Runtime/InteropServices/MemoryMarshal.cs | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs index 4f6edad6786a3..fc590338c225b 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs @@ -27,14 +27,14 @@ private static void ValidateInputOutputSpanNonOverlapping(ReadOnlySpan inp /// Throws an for trying to negate the minimum value of a two-complement value. private static void ThrowNegateTwosCompOverflow() => throw new OverflowException(SR.Overflow_NegateTwosCompNum); - /// Creates a span of from a when they're the same type. + /// Creates a span of from a when they're the same type. private static unsafe Span Rename(Span span) // MemoryMarshal.Cast, but without constraints or validation { Debug.Assert(sizeof(TFrom) == sizeof(TTo)); return MemoryMarshal.CreateSpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); } - /// Creates a span of from a when they're the same type. + /// Creates a span of from a when they're the same type. private static unsafe ReadOnlySpan Rename(ReadOnlySpan span) // MemoryMarshal.Cast, but without constraints or validation { Debug.Assert(sizeof(TFrom) == sizeof(TTo)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs index 8b04c064383d8..0b0da448e4eba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs @@ -122,9 +122,6 @@ public static Span Cast(Span span) if (RuntimeHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); - if (typeof(TFrom) == typeof(TTo)) - return new Span(ref Unsafe.As(ref span._reference), span.Length); - // Use unsigned integers - unsigned division by constant (especially by power of 2) // and checked casts are faster and smaller. uint fromSize = (uint)Unsafe.SizeOf(); @@ -180,9 +177,6 @@ public static ReadOnlySpan Cast(ReadOnlySpan span) if (RuntimeHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); - if (typeof(TFrom) == typeof(TTo)) - return new ReadOnlySpan(ref Unsafe.As(ref span._reference), span.Length); - // Use unsigned integers - unsigned division by constant (especially by power of 2) // and checked casts are faster and smaller. uint fromSize = (uint)Unsafe.SizeOf(); From 38d2b06dc763cc6cc398f266b29ead4e8751eba9 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 18 Mar 2024 11:37:45 -0400 Subject: [PATCH 3/3] Fix build --- .../Tensors/TensorPrimitives.Helpers.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs index fc590338c225b..1ddabb93dd0ea 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Helpers.cs @@ -28,17 +28,27 @@ private static void ValidateInputOutputSpanNonOverlapping(ReadOnlySpan inp private static void ThrowNegateTwosCompOverflow() => throw new OverflowException(SR.Overflow_NegateTwosCompNum); /// Creates a span of from a when they're the same type. - private static unsafe Span Rename(Span span) // MemoryMarshal.Cast, but without constraints or validation + /// + /// This is the same as MemoryMarshal.Cast, except only to be used when TFrom and TTo are the same type or effectively + /// the same type (e.g. int and nint in a 32-bit process). MemoryMarshal.Cast can't currently be used as it's + /// TFrom/TTo are constrained to be value types. + /// + private static unsafe Span Rename(Span span) { Debug.Assert(sizeof(TFrom) == sizeof(TTo)); - return MemoryMarshal.CreateSpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); + return *(Span*)(&span); } /// Creates a span of from a when they're the same type. - private static unsafe ReadOnlySpan Rename(ReadOnlySpan span) // MemoryMarshal.Cast, but without constraints or validation + /// + /// This is the same as MemoryMarshal.Cast, except only to be used when TFrom and TTo are the same type or effectively + /// the same type (e.g. int and nint in a 32-bit process). MemoryMarshal.Cast can't currently be used as it's + /// TFrom/TTo are constrained to be value types. + /// + private static unsafe ReadOnlySpan Rename(ReadOnlySpan span) { Debug.Assert(sizeof(TFrom) == sizeof(TTo)); - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); + return *(ReadOnlySpan*)(&span); } /// Mask used to handle alignment elements before vectorized handling of the input.