diff --git a/src/libraries/System.Linq/src/System/Linq/Sum.cs b/src/libraries/System.Linq/src/System/Linq/Sum.cs index 7eb2dc855dc67b..481997b8f82633 100644 --- a/src/libraries/System.Linq/src/System/Linq/Sum.cs +++ b/src/libraries/System.Linq/src/System/Linq/Sum.cs @@ -60,11 +60,11 @@ private static TResult Sum(ReadOnlySpan span) if (typeof(T) == typeof(long)) { - return (TResult)(object)SumSignedIntegersVectorized(MemoryMarshal.Cast(span)); + return (TResult)(object)SumSignedIntegersVectorized(Unsafe.BitCast, ReadOnlySpan>(span)); } if (typeof(T) == typeof(int)) { - return (TResult)(object)SumSignedIntegersVectorized(MemoryMarshal.Cast(span)); + return (TResult)(object)SumSignedIntegersVectorized(Unsafe.BitCast, ReadOnlySpan>(span)); } } 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 1ddabb93dd0ea4..5e4e89b61742d0 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 @@ -36,7 +36,11 @@ private static void ValidateInputOutputSpanNonOverlapping(ReadOnlySpan inp private static unsafe Span Rename(Span span) { Debug.Assert(sizeof(TFrom) == sizeof(TTo)); +#if NET9_0_OR_GREATER + return Unsafe.BitCast, Span>(span); +#else return *(Span*)(&span); +#endif } /// Creates a span of from a when they're the same type. @@ -48,7 +52,11 @@ private static unsafe Span Rename(Span span) private static unsafe ReadOnlySpan Rename(ReadOnlySpan span) { Debug.Assert(sizeof(TFrom) == sizeof(TTo)); +#if NET9_0_OR_GREATER + return Unsafe.BitCast, ReadOnlySpan>(span); +#else return *(ReadOnlySpan*)(&span); +#endif } /// Mask used to handle alignment elements before vectorized handling of the input. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index db12f89bf2dd18..5192820e1a4d5b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -763,12 +763,12 @@ private static void AppendString(ref ValueListBuilder result, scop { if (typeof(TChar) == typeof(char)) { - result.Append(MemoryMarshal.Cast(s)); + result.Append(Unsafe.BitCast, ReadOnlySpan>(s)); } else { Debug.Assert(typeof(TChar) == typeof(byte)); - Encoding.UTF8.GetBytes(s, MemoryMarshal.Cast(result.AppendSpan(Encoding.UTF8.GetByteCount(s)))); + Encoding.UTF8.GetBytes(s, Unsafe.BitCast, Span>(result.AppendSpan(Encoding.UTF8.GetByteCount(s)))); } } @@ -777,8 +777,8 @@ internal static void FormatFraction(ref ValueListBuilder result, i Span chars = stackalloc TChar[11]; int charCount; bool formatted = typeof(TChar) == typeof(char) ? - fraction.TryFormat(MemoryMarshal.Cast(chars), out charCount, fractionFormat, CultureInfo.InvariantCulture) : - fraction.TryFormat(MemoryMarshal.Cast(chars), out charCount, fractionFormat, CultureInfo.InvariantCulture); + fraction.TryFormat(Unsafe.BitCast, Span>(chars), out charCount, fractionFormat, CultureInfo.InvariantCulture) : + fraction.TryFormat(Unsafe.BitCast, Span>(chars), out charCount, fractionFormat, CultureInfo.InvariantCulture); Debug.Assert(charCount != 0); result.Append(chars.Slice(0, charCount)); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs index e87960b61792d4..f2b99e5a81b3b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs @@ -358,8 +358,8 @@ internal ReadOnlySpan AMDesignatorTChar() where TChar : unmanaged, { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(AMDesignator) : - MemoryMarshal.Cast(amDesignatorUtf8 ??= Encoding.UTF8.GetBytes(AMDesignator)); + Unsafe.BitCast, ReadOnlySpan>(AMDesignator) : + Unsafe.BitCast, ReadOnlySpan>(amDesignatorUtf8 ??= Encoding.UTF8.GetBytes(AMDesignator)); } public Calendar Calendar @@ -607,8 +607,8 @@ internal ReadOnlySpan DateSeparatorTChar() where TChar : unmanaged { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(DateSeparator) : - MemoryMarshal.Cast(dateSeparatorUtf8 ??= Encoding.UTF8.GetBytes(DateSeparator)); + Unsafe.BitCast, ReadOnlySpan>(DateSeparator) : + Unsafe.BitCast, ReadOnlySpan>(dateSeparatorUtf8 ??= Encoding.UTF8.GetBytes(DateSeparator)); } public DayOfWeek FirstDayOfWeek @@ -810,8 +810,8 @@ internal ReadOnlySpan PMDesignatorTChar() where TChar : unmanaged, { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(PMDesignator) : - MemoryMarshal.Cast(pmDesignatorUtf8 ??= Encoding.UTF8.GetBytes(PMDesignator)); + Unsafe.BitCast, ReadOnlySpan>(PMDesignator) : + Unsafe.BitCast, ReadOnlySpan>(pmDesignatorUtf8 ??= Encoding.UTF8.GetBytes(PMDesignator)); } public string RFC1123Pattern => rfc1123Pattern; @@ -992,8 +992,8 @@ internal ReadOnlySpan TimeSeparatorTChar() where TChar : unmanaged { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(TimeSeparator) : - MemoryMarshal.Cast(timeSeparatorUtf8 ??= Encoding.UTF8.GetBytes(TimeSeparator)); + Unsafe.BitCast, ReadOnlySpan>(TimeSeparator) : + Unsafe.BitCast, ReadOnlySpan>(timeSeparatorUtf8 ??= Encoding.UTF8.GetBytes(TimeSeparator)); } public string UniversalSortableDateTimePattern => universalSortableDateTimePattern; @@ -1731,8 +1731,8 @@ internal ReadOnlySpan DecimalSeparatorTChar() where TChar : unmana { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(DecimalSeparator) : - MemoryMarshal.Cast(_decimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(DecimalSeparator)); + Unsafe.BitCast, ReadOnlySpan>(DecimalSeparator) : + Unsafe.BitCast, ReadOnlySpan>(_decimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(DecimalSeparator)); } // Positive TimeSpan Pattern diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs index 0424b678e4b182..60882f7aaaa0ee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs @@ -269,8 +269,8 @@ internal ReadOnlySpan CurrencyDecimalSeparatorTChar() where TChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_currencyDecimalSeparator) : - MemoryMarshal.Cast(_currencyDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_currencyDecimalSeparator)); + Unsafe.BitCast, ReadOnlySpan>(_currencyDecimalSeparator) : + Unsafe.BitCast, ReadOnlySpan>(_currencyDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_currencyDecimalSeparator)); } public bool IsReadOnly => _isReadOnly; @@ -361,8 +361,8 @@ internal ReadOnlySpan CurrencyGroupSeparatorTChar() where TChar : { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_currencyGroupSeparator) : - MemoryMarshal.Cast(_currencyGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_currencyGroupSeparator)); + Unsafe.BitCast, ReadOnlySpan>(_currencyGroupSeparator) : + Unsafe.BitCast, ReadOnlySpan>(_currencyGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_currencyGroupSeparator)); } public string CurrencySymbol @@ -383,8 +383,8 @@ internal ReadOnlySpan CurrencySymbolTChar() where TChar : unmanage { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_currencySymbol) : - MemoryMarshal.Cast(_currencySymbolUtf8 ??= Encoding.UTF8.GetBytes(_currencySymbol)); + Unsafe.BitCast, ReadOnlySpan>(_currencySymbol) : + Unsafe.BitCast, ReadOnlySpan>(_currencySymbolUtf8 ??= Encoding.UTF8.GetBytes(_currencySymbol)); } internal byte[]? CurrencySymbolUtf8 => _currencySymbolUtf8 ??= Encoding.UTF8.GetBytes(_currencySymbol); @@ -429,8 +429,8 @@ internal ReadOnlySpan NaNSymbolTChar() where TChar : unmanaged, IU { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_nanSymbol) : - MemoryMarshal.Cast(_nanSymbolUtf8 ??= Encoding.UTF8.GetBytes(_nanSymbol)); + Unsafe.BitCast, ReadOnlySpan>(_nanSymbol) : + Unsafe.BitCast, ReadOnlySpan>(_nanSymbolUtf8 ??= Encoding.UTF8.GetBytes(_nanSymbol)); } public int CurrencyNegativePattern @@ -514,8 +514,8 @@ internal ReadOnlySpan NegativeInfinitySymbolTChar() where TChar : { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_negativeInfinitySymbol) : - MemoryMarshal.Cast(_negativeInfinitySymbolUtf8 ??= Encoding.UTF8.GetBytes(_negativeInfinitySymbol)); + Unsafe.BitCast, ReadOnlySpan>(_negativeInfinitySymbol) : + Unsafe.BitCast, ReadOnlySpan>(_negativeInfinitySymbolUtf8 ??= Encoding.UTF8.GetBytes(_negativeInfinitySymbol)); } public string NegativeSign @@ -537,8 +537,8 @@ internal ReadOnlySpan NegativeSignTChar() where TChar : unmanaged, { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_negativeSign) : - MemoryMarshal.Cast(_negativeSignUtf8 ??= Encoding.UTF8.GetBytes(_negativeSign)); + Unsafe.BitCast, ReadOnlySpan>(_negativeSign) : + Unsafe.BitCast, ReadOnlySpan>(_negativeSignUtf8 ??= Encoding.UTF8.GetBytes(_negativeSign)); } public int NumberDecimalDigits @@ -573,8 +573,8 @@ internal ReadOnlySpan NumberDecimalSeparatorTChar() where TChar : { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_numberDecimalSeparator) : - MemoryMarshal.Cast(_numberDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_numberDecimalSeparator)); + Unsafe.BitCast, ReadOnlySpan>(_numberDecimalSeparator) : + Unsafe.BitCast, ReadOnlySpan>(_numberDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_numberDecimalSeparator)); } public string NumberGroupSeparator @@ -594,8 +594,8 @@ internal ReadOnlySpan NumberGroupSeparatorTChar() where TChar : un { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_numberGroupSeparator) : - MemoryMarshal.Cast(_numberGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_numberGroupSeparator)); + Unsafe.BitCast, ReadOnlySpan>(_numberGroupSeparator) : + Unsafe.BitCast, ReadOnlySpan>(_numberGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_numberGroupSeparator)); } public int CurrencyPositivePattern @@ -631,8 +631,8 @@ internal ReadOnlySpan PositiveInfinitySymbolTChar() where TChar : { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_positiveInfinitySymbol) : - MemoryMarshal.Cast(_positiveInfinitySymbolUtf8 ??= Encoding.UTF8.GetBytes(_positiveInfinitySymbol)); + Unsafe.BitCast, ReadOnlySpan>(_positiveInfinitySymbol) : + Unsafe.BitCast, ReadOnlySpan>(_positiveInfinitySymbolUtf8 ??= Encoding.UTF8.GetBytes(_positiveInfinitySymbol)); } public string PositiveSign @@ -654,8 +654,8 @@ internal ReadOnlySpan PositiveSignTChar() where TChar : unmanaged, { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_positiveSign) : - MemoryMarshal.Cast(_positiveSignUtf8 ??= Encoding.UTF8.GetBytes(_positiveSign)); + Unsafe.BitCast, ReadOnlySpan>(_positiveSign) : + Unsafe.BitCast, ReadOnlySpan>(_positiveSignUtf8 ??= Encoding.UTF8.GetBytes(_positiveSign)); } public int PercentDecimalDigits @@ -690,8 +690,8 @@ internal ReadOnlySpan PercentDecimalSeparatorTChar() where TChar : { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_percentDecimalSeparator) : - MemoryMarshal.Cast(_percentDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_percentDecimalSeparator)); + Unsafe.BitCast, ReadOnlySpan>(_percentDecimalSeparator) : + Unsafe.BitCast, ReadOnlySpan>(_percentDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_percentDecimalSeparator)); } public string PercentGroupSeparator @@ -711,8 +711,8 @@ internal ReadOnlySpan PercentGroupSeparatorTChar() where TChar : u { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_percentGroupSeparator) : - MemoryMarshal.Cast(_percentGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_percentGroupSeparator)); + Unsafe.BitCast, ReadOnlySpan>(_percentGroupSeparator) : + Unsafe.BitCast, ReadOnlySpan>(_percentGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_percentGroupSeparator)); } public string PercentSymbol @@ -732,8 +732,8 @@ internal ReadOnlySpan PercentSymbolTChar() where TChar : unmanaged { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_percentSymbol) : - MemoryMarshal.Cast(_percentSymbolUtf8 ??= Encoding.UTF8.GetBytes(_percentSymbol)); + Unsafe.BitCast, ReadOnlySpan>(_percentSymbol) : + Unsafe.BitCast, ReadOnlySpan>(_percentSymbolUtf8 ??= Encoding.UTF8.GetBytes(_percentSymbol)); } public string PerMilleSymbol @@ -754,8 +754,8 @@ internal ReadOnlySpan PerMilleSymbolTChar() where TChar : unmanage { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); return typeof(TChar) == typeof(char) ? - MemoryMarshal.Cast(_perMilleSymbol) : - MemoryMarshal.Cast(_perMilleSymbolUtf8 ??= Encoding.UTF8.GetBytes(_perMilleSymbol)); + Unsafe.BitCast, ReadOnlySpan>(_perMilleSymbol) : + Unsafe.BitCast, ReadOnlySpan>(_perMilleSymbolUtf8 ??= Encoding.UTF8.GetBytes(_perMilleSymbol)); } public string[] NativeDigits diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index 928eaea0d9540a..38a752c6810ffe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -621,7 +621,7 @@ private static bool TryCopyTo(string source, Span destination, out if (typeof(TChar) == typeof(char)) { - if (source.TryCopyTo(MemoryMarshal.Cast(destination))) + if (source.TryCopyTo(Unsafe.BitCast, Span>(destination))) { charsWritten = source.Length; return true; @@ -632,7 +632,7 @@ private static bool TryCopyTo(string source, Span destination, out } else { - return Encoding.UTF8.TryGetBytes(source, MemoryMarshal.Cast(destination), out charsWritten); + return Encoding.UTF8.TryGetBytes(source, Unsafe.BitCast, Span>(destination), out charsWritten); } } 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 263f60ad0ba10f..f15712bf5fe92e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -895,16 +895,16 @@ internal static bool SpanStartsWith(ReadOnlySpan span, ReadOnlySpa { if (typeof(TChar) == typeof(char)) { - ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); - ReadOnlySpan typedValue = MemoryMarshal.Cast(value); + ReadOnlySpan typedSpan = Unsafe.BitCast, ReadOnlySpan>(span); + ReadOnlySpan typedValue = Unsafe.BitCast, ReadOnlySpan>(value); return typedSpan.StartsWith(typedValue, comparisonType); } else { Debug.Assert(typeof(TChar) == typeof(byte)); - ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); - ReadOnlySpan typedValue = MemoryMarshal.Cast(value); + ReadOnlySpan typedSpan = Unsafe.BitCast, ReadOnlySpan>(span); + ReadOnlySpan typedValue = Unsafe.BitCast, ReadOnlySpan>(value); return typedSpan.StartsWithUtf8(typedValue, comparisonType); } } @@ -915,13 +915,13 @@ internal static ReadOnlySpan SpanTrim(ReadOnlySpan span) { if (typeof(TChar) == typeof(char)) { - return MemoryMarshal.Cast(MemoryMarshal.Cast(span).Trim()); + return Unsafe.BitCast, ReadOnlySpan>(Unsafe.BitCast, ReadOnlySpan>(span).Trim()); } else { Debug.Assert(typeof(TChar) == typeof(byte)); - return MemoryMarshal.Cast(MemoryMarshal.Cast(span).TrimUtf8()); + return Unsafe.BitCast, ReadOnlySpan>(Unsafe.BitCast, ReadOnlySpan>(span).TrimUtf8()); } } @@ -931,16 +931,16 @@ internal static bool SpanEqualsOrdinalIgnoreCase(ReadOnlySpan span { if (typeof(TChar) == typeof(char)) { - ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); - ReadOnlySpan typedValue = MemoryMarshal.Cast(value); + ReadOnlySpan typedSpan = Unsafe.BitCast, ReadOnlySpan>(span); + ReadOnlySpan typedValue = Unsafe.BitCast, ReadOnlySpan>(value); return typedSpan.EqualsOrdinalIgnoreCase(typedValue); } else { Debug.Assert(typeof(TChar) == typeof(byte)); - ReadOnlySpan typedSpan = MemoryMarshal.Cast(span); - ReadOnlySpan typedValue = MemoryMarshal.Cast(value); + ReadOnlySpan typedSpan = Unsafe.BitCast, ReadOnlySpan>(span); + ReadOnlySpan typedValue = Unsafe.BitCast, ReadOnlySpan>(value); return typedSpan.EqualsOrdinalIgnoreCaseUtf8(typedValue); } } @@ -1053,7 +1053,7 @@ internal static void ThrowFormatException(ReadOnlySpan value) // It's possible after we check the bytes for validity that they could be concurrently // mutated, but if that's happening, all bets are off, anyway, and it simply impacts // which exception is thrown. - ReadOnlySpan bytes = MemoryMarshal.Cast(value); + ReadOnlySpan bytes = Unsafe.BitCast, ReadOnlySpan>(value); errorMessage = Utf8.IsValid(bytes) ? SR.Format(SR.Format_InvalidStringWithValue, Encoding.UTF8.GetString(bytes)) : SR.Format_InvalidString; diff --git a/src/libraries/System.Private.CoreLib/src/System/Version.cs b/src/libraries/System.Private.CoreLib/src/System/Version.cs index 99c26d59a91439..607a72d03fb9aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Version.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Version.cs @@ -249,8 +249,8 @@ static void ThrowArgumentException(string failureUpperBound) => int valueCharsWritten; bool formatted = typeof(TChar) == typeof(char) ? - ((uint)value).TryFormat(MemoryMarshal.Cast(destination), out valueCharsWritten) : - ((uint)value).TryFormat(MemoryMarshal.Cast(destination), out valueCharsWritten, default, CultureInfo.InvariantCulture); + ((uint)value).TryFormat(Unsafe.BitCast, Span>(destination), out valueCharsWritten) : + ((uint)value).TryFormat(Unsafe.BitCast, Span>(destination), out valueCharsWritten, default, CultureInfo.InvariantCulture); if (!formatted) { diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs index 8d6b7abbfdbd9b..a62190ee40d933 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs @@ -2241,8 +2241,8 @@ private bool TryFormatCore(Span destination, out int charsWritten, { int realChars; if (typeof(TChar) == typeof(char) ? - m_real.TryFormat(MemoryMarshal.Cast(destination.Slice(1)), out realChars, format, provider) : - m_real.TryFormat(MemoryMarshal.Cast(destination.Slice(1)), out realChars, format, provider)) + m_real.TryFormat(Unsafe.BitCast, Span>(destination.Slice(1)), out realChars, format, provider) : + m_real.TryFormat(Unsafe.BitCast, Span>(destination.Slice(1)), out realChars, format, provider)) { destination[0] = TChar.CreateTruncating('<'); destination = destination.Slice(1 + realChars); // + 1 for < @@ -2252,8 +2252,8 @@ private bool TryFormatCore(Span destination, out int charsWritten, { int imaginaryChars; if (typeof(TChar) == typeof(char) ? - m_imaginary.TryFormat(MemoryMarshal.Cast(destination.Slice(2)), out imaginaryChars, format, provider) : - m_imaginary.TryFormat(MemoryMarshal.Cast(destination.Slice(2)), out imaginaryChars, format, provider)) + m_imaginary.TryFormat(Unsafe.BitCast, Span>(destination.Slice(2)), out imaginaryChars, format, provider) : + m_imaginary.TryFormat(Unsafe.BitCast, Span>(destination.Slice(2)), out imaginaryChars, format, provider)) { // We have 1 more character for: > if ((uint)(2 + imaginaryChars) < (uint)destination.Length) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs index 3df92cd8609358..8cc4f236af4de4 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs @@ -1263,10 +1263,9 @@ public static unsafe void BitCast() // Conversion between same sized ref structs should succeed - // TODO https://github.com/dotnet/runtime/issues/102988: Uncomment once this works on mono - //int i = 42; - //Span span = Unsafe.BitCast, Span>(new ReadOnlySpan(&i, 1)); - //Assert.Equal(42, span[0]); + int i = 42; + Span span = Unsafe.BitCast, Span>(new ReadOnlySpan(&i, 1)); + Assert.Equal(42, span[0]); // Conversion from runtime SIMD type to a custom struct should succeed diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 49bc8d1b45dd58..3d94b23c05da04 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -4803,6 +4803,15 @@ interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *fi } } +static MonoClass* +get_class_from_token (MonoGenericContext *generic_context, MonoMethod *cur_method, uint32_t token) +{ + if (cur_method->wrapper_type != MONO_WRAPPER_NONE) + return (MonoClass *)mono_method_get_wrapper_data (cur_method, token); + else + return mini_get_class (cur_method, token, generic_context); +} + static gboolean interp_handle_box_patterns (TransformData *td, MonoClass *box_class, const unsigned char *end, MonoImage *image, MonoGenericContext *generic_context, MonoError *error) { @@ -4827,6 +4836,91 @@ interp_handle_box_patterns (TransformData *td, MonoClass *box_class, const unsig td->ip = next_ip + 5; return TRUE; } + if (m_class_is_byreflike (box_class)) { + if (*next_ip == CEE_BRTRUE || *next_ip == CEE_BRTRUE_S || *next_ip == CEE_BRFALSE || *next_ip == CEE_BRFALSE_S) { + // replace + // box ByRefLike + // [brtrue/brfalse] + // + // by + // + // ldc.i4.s 1 + // [brtrue/brfalse] + td->sp--; + interp_add_ins (td, MINT_LDC_I4_S); + td->last_ins->data[0] = (guint16) 1; + push_simple_type (td, STACK_TYPE_I4); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); + td->ip += 5; + // skip over box, continue with the branch opcode + return TRUE; + } + if (*next_ip == CEE_ISINST) { + // box !!T + // isinst S + // [brtrue/brfalse] + // + // turns into + // ldc.i4.s (0 or 1) + // [brtrue/brfalse] + + // and + + // box !!T + // isinst S + // unbox.any S + // + // turns into + // nop + // -or- + // ldnull + // cknull + const unsigned char *second_ip = next_ip + 5; + if (second_ip >= end || !interp_ip_in_cbb (td, GPTRDIFF_TO_INT (second_ip - td->il_code))) { + return FALSE; + } + + uint32_t isinst_token = read32 (next_ip + 1); + MonoClass *isinst_klass = get_class_from_token (generic_context, method, isinst_token); + + CHECK_TYPELOAD (isinst_klass); + + gboolean isinst = mono_class_is_assignable_from_internal (isinst_klass, box_class); + + if (*second_ip == CEE_BRTRUE || *second_ip == CEE_BRTRUE_S || *second_ip == CEE_BRFALSE || *second_ip == CEE_BRFALSE_S) { + td->sp--; + interp_add_ins (td, MINT_LDC_I4_S); + td->last_ins->data[0] = (guint16) (isinst ? 1 : 0); + push_simple_type (td, STACK_TYPE_I4); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); + td->ip = next_ip + 5; + // skip over the box and isinst opcodes, continue with the branch opcode + return TRUE; + } + if (*second_ip == CEE_UNBOX_ANY) { + uint32_t unbox_any_token = read32 (second_ip + 1); + MonoClass *unbox_klass = get_class_from_token (generic_context, method, unbox_any_token); + CHECK_TYPELOAD (unbox_klass); + if (unbox_klass == isinst_klass) { + if (isinst) { + // leave the original value unchanged on the stack + interp_add_ins (td, MINT_NOP); + td->ip = second_ip + 5; + // skip over all three opcodes, continue with the next opcode; + return TRUE; + } else { + // box !T ; isinst S ; unbox.any S should always be + // under a box !T ; isinst S ; brtrue/brfalse branch + // where the types match. So if they don't, that's + // an invalid program. In that case compile the + // code sequence as is, and allow "box ByRefLike" to + // throw InvalidProgramException at execution-time + } + } + } + } + } +exit: return FALSE; } @@ -7158,14 +7252,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, /* already boxed, do nothing. */ td->ip += 5; } else { - if (G_UNLIKELY (m_class_is_byreflike (klass)) && !td->optimized) { - if (td->verbose_level) - g_print ("Box byreflike detected. Retry compilation with full optimization.\n"); - td->retry_compilation = TRUE; - td->retry_with_inlining = TRUE; - goto exit; - } - const gboolean vt = mono_mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT; if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) diff --git a/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il index 8480a5cf452079..02a9037ca10ae6 100644 --- a/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il +++ b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il @@ -237,6 +237,20 @@ ret } + .method public hidebysig + instance bool BoxIsinstUnboxAny_Mismatch(!T) cil managed + { + ldarg.1 + // Begin sequence + box !T + isinst ByRefLikeType2 + unbox.any ByRefLikeType2 + // End sequence + pop + ldc.i4.0 + ret + } + .method public hidebysig instance bool BoxIsinstBranch(!T) cil managed { @@ -443,6 +457,14 @@ ) } +.class public sequential ansi sealed beforefieldinit ByRefLikeType2 + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) +} + .class interface public auto ansi abstract InvalidCSharp.EmptyInterface { } @@ -755,6 +777,22 @@ ret } + .method public hidebysig static + bool BoxIsinstUnboxAny_Mismatch() cil managed + { + .locals init ( + [0] valuetype InvalidCSharp.GenericByRefLike_Over`1 + ) + + ldloca.s 0 + initobj valuetype InvalidCSharp.GenericByRefLike_Over`1 + ldloca.s 0 + ldloc 0 + ldfld !0 valuetype InvalidCSharp.GenericByRefLike_Over`1::Field + call instance bool valuetype InvalidCSharp.GenericByRefLike_Over`1::BoxIsinstUnboxAny_Mismatch(!0) + ret + } + .method public hidebysig static bool BoxIsinstBranch() cil managed { diff --git a/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs b/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs index 45de0af7d211ba..280a567f2deb84 100644 --- a/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs +++ b/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs @@ -67,6 +67,16 @@ public static void Validate_RecognizedOpCodeSequences_Scenarios() Assert.True(Exec.BoxIsinstBranch()); } + [Fact] + public static void Validate_RecognizedOpCodeSequences_Mismatch() + { + Console.WriteLine($"{nameof(Validate_RecognizedOpCodeSequences_Mismatch)}..."); + + // box !T ; isinst S ; unbox.any S should always be guarded by a box !T ; isinst S; + // brtrue/brfalse branch, so if it's ever executed and the types aren't equal that's invalid + Assert.Throws(() => { Exec.BoxIsinstUnboxAny_Mismatch(); }); + } + [Fact] public static void Validate_InvalidOpCode_Scenarios() { @@ -116,4 +126,4 @@ public static void Validate_MemberDiscoveryViaReflection_ForSpanReadOnlySpan() // Assert.NotNull(m); // } } -} \ No newline at end of file +}