From 606fa6b302dcbf6d5131accf14636da107c531b0 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 09:04:16 -0800 Subject: [PATCH 01/13] Adding System.Runtime.CompilerServices.Unsafe.BitCast --- .../System/Runtime/CompilerServices/Unsafe.cs | 18 +++++++++ .../tests/UnsafeTests.cs | 38 +++++++++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 1 + 3 files changed, 57 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs index c4c3f66981954..a82d974884aaf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs @@ -238,6 +238,24 @@ public static bool AreSame([AllowNull] ref T left, [AllowNull] ref T right) // ret } + /// + /// Reinterprets the given reference as a reference to a value of type . + /// + /// The size of and are not the same. + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TTo BitCast(TFrom source) + where TFrom : struct + where TTo : struct + { + if (sizeof(TFrom) != sizeof(TTo)) + { + ThrowHelper.ThrowNotSupportedException(); + } + return As(ref source); + } + /// /// Copies a value of type T to the given location. /// diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 4780b41b00ae8..c0171353f3096 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Numerics; using System.Runtime.InteropServices; using Xunit; @@ -1079,6 +1080,35 @@ public static unsafe void NullRef() Assert.Throws(() => Unsafe.NullRef() = 42); Assert.Throws(() => Unsafe.NullRef()); } + + [Fact] + public static unsafe void BitCast() + { + Assert.Throws(() => Unsafe.BitCast(5)); + Assert.Throws(() => Unsafe.BitCast(5)); + + Assert.Equal(0x8000_0000u, Unsafe.BitCast(-0.0f)); + Assert.Equal(float.PositiveInfinity, Unsafe.BitCast(0x7F80_0000u)); + + Assert.Equal(int.MinValue, Unsafe.BitCast(0x8000_0000u)); + Assert.Equal(0x8000_0000u, Unsafe.BitCast(int.MinValue)); + + Vector4 vector4a = new Vector4(1.0f, 2.0f, 3.0f, 4.0f); + Single4 single4a = Unsafe.BitCast(vector4a); + + Assert.Equal(1.0f, single4a.X); + Assert.Equal(2.0f, single4a.Y); + Assert.Equal(3.0f, single4a.Z); + Assert.Equal(4.0f, single4a.W); + + Single4 single4b = new Single4 { X = -1.0f, Y = -2.0f, Z = -3.0f, W = -4.0f }; + Vector4 vector4b = Unsafe.BitCast(single4b); + + Assert.Equal(-1.0f, vector4b.X); + Assert.Equal(-2.0f, vector4b.Y); + Assert.Equal(-3.0f, vector4b.Z); + Assert.Equal(-4.0f, vector4b.W); + } } [StructLayout(LayoutKind.Explicit)] @@ -1155,4 +1185,12 @@ public struct StringInt32 public string String; public int Int32; } + + public struct Single4 + { + public float X; + public float Y; + public float Z; + public float W; + } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 2f0d63ca45cba..aea5e9a20a96e 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -12787,6 +12787,7 @@ public static partial class Unsafe [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("o")] public static T? As(object? o) where T : class? { throw null; } public static ref TTo As(ref TFrom source) { throw null; } + public static TTo BitCast(TFrom source) where TFrom : struct where TTo : struct { throw null; } public static System.IntPtr ByteOffset([System.Diagnostics.CodeAnalysis.AllowNull] ref T origin, [System.Diagnostics.CodeAnalysis.AllowNull] ref T target) { throw null; } [System.CLSCompliantAttribute(false)] public static void CopyBlock(ref byte destination, ref byte source, uint byteCount) { } From 978898077209794bab8a2c764574fc55ed23e4b4 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 11:08:35 -0800 Subject: [PATCH 02/13] Adding some basic intrinsic recognition for Unsafe.BitCast --- src/coreclr/jit/fgbasic.cpp | 1 + src/coreclr/jit/importercalls.cpp | 160 +++++++++++++++++++++++++++ src/coreclr/jit/namedintrinsiclist.h | 1 + 3 files changed, 162 insertions(+) diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 581a5aa620bef..c008337c71ade 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -1484,6 +1484,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed #endif // FEATURE_HW_INTRINSICS case NI_SRCS_UNSAFE_As: case NI_SRCS_UNSAFE_AsRef: + case NI_SRCS_UNSAFE_BitCast: case NI_SRCS_UNSAFE_SkipInit: { // TODO-CQ: These are no-ops in that they never produce any IR diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index c57ecc3c71952..f6f9fdcb1f496 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4002,6 +4002,162 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, return impPopStack().val; } + case NI_SRCS_UNSAFE_BitCast: + { + assert(sig->sigInst.methInstCount == 2); + + CORINFO_CLASS_HANDLE fromTypeHnd = sig->sigInst.methInst[0]; + CORINFO_CLASS_HANDLE toTypeHnd = sig->sigInst.methInst[0]; + + if (fromTypeHnd == toTypeHnd) + { + // Handle the easy case of matching type handles, such as `int` to `int` + return impPopStack().val; + } + + unsigned fromSize = info.compCompHnd->getClassSize(fromTypeHnd); + unsigned toSize = info.compCompHnd->getClassSize(toTypeHnd); + + if (fromSize != toSize) + { + // Fallback to the software implementation to throw when sizes don't match + return nullptr; + } + + CorInfoType fromJitType = info.compCompHnd->asCorInfoType(fromTypeHnd); + var_types fromType = JitType2PreciseVarType(fromJitType); + + CorInfoType toJitType = info.compCompHnd->asCorInfoType(toTypeHnd); + var_types toType = JitType2PreciseVarType(toJitType); + + bool involvesStructType = false; + +#if FEATURE_SIMD + if (fromType == TYP_STRUCT) + { + unsigned simdSize = getSIMDTypeSizeInBytes(toTypeHnd); + + if (simdSize != 0) + { + fromType = getSIMDTypeForSize(simdSize); + } + else + { + involvesStructType = true; + } + } + + if (toType == TYP_STRUCT) + { + unsigned simdSize = getSIMDTypeSizeInBytes(toTypeHnd); + + if (simdSize != 0) + { + toType = getSIMDTypeForSize(simdSize); + } + else + { + involvesStructType = true; + } + } +#else + involvesStructType = (fromType == TYP_STRUCT) || (toType == TYP_STRUCT); +#endif // FEATURE_SIMD + + if (involvesStructType) + { + // Handle the complex case where TFrom or TTo involves a non-special TYP_STRUCT + // TODO-CQ: There is quite a bit of specialization we "could" do here. However, + // due to ABI differences and other special considerations this isn't always trivial + return nullptr; + } + + if (varTypeIsSIMD(fromType)) + { + // Handle bitcasting for same sized simd, such as `Vector128` to `Vector128` + assert(varTypeIsSIMD(toType) && (fromType == toType)); + return impPopStack().val; + } + + if (varTypeIsFloating(fromType)) + { + // Handle bitcasting from floating to same sized integral, such as `float` to `int` + assert(varTypeIsIntegral(toType)); + +#if !TARGET_64BIT + if ((fromType == TYP_DOUBLE) && !impStackTop().val->IsCnsFltOrDbl()) + { + // TODO-Cleanup: We should support this on 32-bit but it requires decomposition work + return nullptr; + } +#endif // !TARGET_64BIT + + GenTree* op1 = impPopStack().val; + + if (op1->IsCnsFltOrDbl()) + { + if (fromType == TYP_DOUBLE) + { + double f64Cns = static_cast(op1->AsDblCon()->DconValue()); + return gtNewLconNode(static_cast(BitOperations::DoubleToUInt64Bits(f64Cns))); + } + else + { + assert(fromType == TYP_FLOAT); + float f32Cns = static_cast(op1->AsDblCon()->DconValue()); + return gtNewIconNode(static_cast(BitOperations::SingleToUInt32Bits(f32Cns))); + } + } + else + { + toType = varTypeToSigned(toType); + op1 = impImplicitR4orR8Cast(op1, fromType); + return gtNewBitCastNode(toType, op1); + } + break; + } + + if (varTypeIsFloating(toType)) + { + // Handle bitcasting from integral to same sized floating, such as `int` to `float` + assert(varTypeIsIntegral(fromType)); + +#if !TARGET_64BIT + if ((toType == TYP_DOUBLE) && !impStackTop().val->IsIntegralConst()) + { + // TODO-Cleanup: We should support this on 32-bit but it requires decomposition work + return nullptr; + } +#endif // !TARGET_64BIT + + GenTree* op1 = impPopStack().val; + + if (op1->IsIntegralConst()) + { + if (toType == TYP_DOUBLE) + { + uint64_t u64Cns = static_cast(op1->AsIntConCommon()->LngValue()); + return gtNewDconNode(BitOperations::UInt64BitsToDouble(u64Cns), TYP_DOUBLE); + } + else + { + assert(toType == TYP_FLOAT); + + uint32_t u32Cns = static_cast(op1->AsIntConCommon()->IconValue()); + return gtNewDconNode(BitOperations::UInt32BitsToSingle(u32Cns), TYP_FLOAT); + } + } + else + { + return gtNewBitCastNode(toType, op1); + } + break; + } + + // Handle bitcasting for same sized integrals, such as `int` to `uint` + return impPopStack().val; + } + case NI_SRCS_UNSAFE_ByteOffset: { assert(sig->sigInst.methInstCount == 1); @@ -8202,6 +8358,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_SRCS_UNSAFE_AsRef; } + else if (strcmp(methodName, "BitCast") == 0) + { + result = NI_SRCS_UNSAFE_BitCast; + } else if (strcmp(methodName, "ByteOffset") == 0) { result = NI_SRCS_UNSAFE_ByteOffset; diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 4f922bcb234fd..b80a1b3254aef 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -179,6 +179,7 @@ enum NamedIntrinsic : unsigned short NI_SRCS_UNSAFE_As, NI_SRCS_UNSAFE_AsPointer, NI_SRCS_UNSAFE_AsRef, + NI_SRCS_UNSAFE_BitCast, NI_SRCS_UNSAFE_ByteOffset, NI_SRCS_UNSAFE_Copy, NI_SRCS_UNSAFE_CopyBlock, From a8bd8a6ce58314bd56874fde7cfc9604c14b7fbe Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 12:54:19 -0800 Subject: [PATCH 03/13] Use ClassLayout::AreCompatible as part of Unsafe.BitCast --- src/coreclr/jit/importercalls.cpp | 51 ++++++++++--------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index f6f9fdcb1f496..1f82084d7a9e1 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4025,60 +4025,41 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, } CorInfoType fromJitType = info.compCompHnd->asCorInfoType(fromTypeHnd); - var_types fromType = JitType2PreciseVarType(fromJitType); + var_types fromType = JITtype2varType(fromJitType); CorInfoType toJitType = info.compCompHnd->asCorInfoType(toTypeHnd); - var_types toType = JitType2PreciseVarType(toJitType); + var_types toType = JITtype2varType(toJitType); bool involvesStructType = false; -#if FEATURE_SIMD if (fromType == TYP_STRUCT) { - unsigned simdSize = getSIMDTypeSizeInBytes(toTypeHnd); + involvesStructType = true; - if (simdSize != 0) + if (toType == TYP_STRUCT) { - fromType = getSIMDTypeForSize(simdSize); - } - else - { - involvesStructType = true; + ClassLayout* fromLayout = typGetObjLayout(fromTypeHnd); + ClassLayout* toLayout = typGetObjLayout(toTypeHnd); + + if (ClassLayout::AreCompatible(fromLayout, toLayout)) + { + // Handle compatible struct layouts where we can simply return op1 + return impPopStack().val; + } } } - - if (toType == TYP_STRUCT) + else if (toType == TYP_STRUCT) { - unsigned simdSize = getSIMDTypeSizeInBytes(toTypeHnd); - - if (simdSize != 0) - { - toType = getSIMDTypeForSize(simdSize); - } - else - { - involvesStructType = true; - } + involvesStructType = true; } -#else - involvesStructType = (fromType == TYP_STRUCT) || (toType == TYP_STRUCT); -#endif // FEATURE_SIMD if (involvesStructType) { - // Handle the complex case where TFrom or TTo involves a non-special TYP_STRUCT - // TODO-CQ: There is quite a bit of specialization we "could" do here. However, - // due to ABI differences and other special considerations this isn't always trivial + // TODO-CQ: Handle this by getting the address of `op1` and then dereferencing + // that as TTo, much as `Unsafe.As(ref op1)` would work. return nullptr; } - if (varTypeIsSIMD(fromType)) - { - // Handle bitcasting for same sized simd, such as `Vector128` to `Vector128` - assert(varTypeIsSIMD(toType) && (fromType == toType)); - return impPopStack().val; - } - if (varTypeIsFloating(fromType)) { // Handle bitcasting from floating to same sized integral, such as `float` to `int` From 38e4b87d2f4796366e0cda6a74491a2bf754da81 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 13:03:07 -0800 Subject: [PATCH 04/13] Fixup BitConverter to use Unsafe.BitCast --- .../src/System/BitConverter.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs b/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs index f8e965bf4ed82..b9dd9d938b420 100644 --- a/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs @@ -764,7 +764,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// The number to convert. /// A 64-bit signed integer whose bits are identical to . [Intrinsic] - public static unsafe long DoubleToInt64Bits(double value) => *((long*)&value); + public static unsafe long DoubleToInt64Bits(double value) => Unsafe.BitCast(value); /// /// Converts the specified 64-bit signed integer to a double-precision floating point number. @@ -772,7 +772,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// The number to convert. /// A double-precision floating point number whose bits are identical to . [Intrinsic] - public static unsafe double Int64BitsToDouble(long value) => *((double*)&value); + public static unsafe double Int64BitsToDouble(long value) => Unsafe.BitCast(value); /// /// Converts the specified single-precision floating point number to a 32-bit signed integer. @@ -780,7 +780,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// The number to convert. /// A 32-bit signed integer whose bits are identical to . [Intrinsic] - public static unsafe int SingleToInt32Bits(float value) => *((int*)&value); + public static unsafe int SingleToInt32Bits(float value) => Unsafe.BitCast(value); /// /// Converts the specified 32-bit signed integer to a single-precision floating point number. @@ -788,7 +788,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// The number to convert. /// A single-precision floating point number whose bits are identical to . [Intrinsic] - public static unsafe float Int32BitsToSingle(int value) => *((float*)&value); + public static unsafe float Int32BitsToSingle(int value) => Unsafe.BitCast(value); /// /// Converts the specified half-precision floating point number to a 16-bit signed integer. @@ -813,7 +813,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// A 64-bit unsigned integer whose bits are identical to . [CLSCompliant(false)] [Intrinsic] - public static unsafe ulong DoubleToUInt64Bits(double value) => *((ulong*)&value); + public static unsafe ulong DoubleToUInt64Bits(double value) => Unsafe.BitCast(value); /// /// Converts the specified 64-bit unsigned integer to a double-precision floating point number. @@ -822,7 +822,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// A double-precision floating point number whose bits are identical to . [CLSCompliant(false)] [Intrinsic] - public static unsafe double UInt64BitsToDouble(ulong value) => *((double*)&value); + public static unsafe double UInt64BitsToDouble(ulong value) => Unsafe.BitCast(value); /// /// Converts the specified single-precision floating point number to a 32-bit unsigned integer. @@ -831,7 +831,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// A 32-bit unsigned integer whose bits are identical to . [CLSCompliant(false)] [Intrinsic] - public static unsafe uint SingleToUInt32Bits(float value) => *((uint*)&value); + public static unsafe uint SingleToUInt32Bits(float value) => Unsafe.BitCast(value); /// /// Converts the specified 32-bit unsigned integer to a single-precision floating point number. @@ -840,7 +840,7 @@ public static bool ToBoolean(ReadOnlySpan value) /// A single-precision floating point number whose bits are identical to . [CLSCompliant(false)] [Intrinsic] - public static unsafe float UInt32BitsToSingle(uint value) => *((float*)&value); + public static unsafe float UInt32BitsToSingle(uint value) => Unsafe.BitCast(value); /// /// Converts the specified half-precision floating point number to a 16-bit unsigned integer. From 5cdf971f655b24b7c94b62eb9ea871bcd3902bf1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 13:10:37 -0800 Subject: [PATCH 05/13] Fixup Enum to use Unsafe.BitCast --- .../System.Private.CoreLib/src/System/Enum.cs | 156 +++++++++--------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index 534823f28956f..2fc820c4ee6d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -46,20 +46,20 @@ public abstract partial class Enum : ValueType, IComparable, ISpanFormattable, I RuntimeType rt = (RuntimeType)typeof(TEnum); Type underlyingType = typeof(TEnum).GetEnumUnderlyingType(); - if (underlyingType == typeof(sbyte)) return GetNameInlined(GetEnumInfo(rt), *(sbyte*)&value); - if (underlyingType == typeof(byte)) return GetNameInlined(GetEnumInfo(rt), *(byte*)&value); - if (underlyingType == typeof(short)) return GetNameInlined(GetEnumInfo(rt), *(short*)&value); - if (underlyingType == typeof(ushort)) return GetNameInlined(GetEnumInfo(rt), *(ushort*)&value); - if (underlyingType == typeof(int)) return GetNameInlined(GetEnumInfo(rt), *(int*)&value); - if (underlyingType == typeof(uint)) return GetNameInlined(GetEnumInfo(rt), *(uint*)&value); - if (underlyingType == typeof(long)) return GetNameInlined(GetEnumInfo(rt), *(long*)&value); - if (underlyingType == typeof(ulong)) return GetNameInlined(GetEnumInfo(rt), *(ulong*)&value); + if (underlyingType == typeof(sbyte)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(byte)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(short)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(ushort)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(int)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(uint)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(long)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(ulong)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return GetNameInlined(GetEnumInfo(rt), *(nint*)&value); - if (underlyingType == typeof(nuint)) return GetNameInlined(GetEnumInfo(rt), *(nuint*)&value); - if (underlyingType == typeof(float)) return GetNameInlined(GetEnumInfo(rt), *(float*)&value); - if (underlyingType == typeof(double)) return GetNameInlined(GetEnumInfo(rt), *(double*)&value); - if (underlyingType == typeof(char)) return GetNameInlined(GetEnumInfo(rt), *(char*)&value); + if (underlyingType == typeof(nint)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(nuint)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(float)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(double)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(char)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); #endif throw CreateUnknownEnumTypeException(); } @@ -473,20 +473,20 @@ public static unsafe bool IsDefined(TEnum value) where TEnum : struct, En RuntimeType rt = (RuntimeType)typeof(TEnum); Type underlyingType = typeof(TEnum).GetEnumUnderlyingType(); - if (underlyingType == typeof(sbyte)) return IsDefinedPrimitive(rt, *(sbyte*)&value); - if (underlyingType == typeof(byte)) return IsDefinedPrimitive(rt, *(byte*)&value); - if (underlyingType == typeof(short)) return IsDefinedPrimitive(rt, *(short*)&value); - if (underlyingType == typeof(ushort)) return IsDefinedPrimitive(rt, *(ushort*)&value); - if (underlyingType == typeof(int)) return IsDefinedPrimitive(rt, *(int*)&value); - if (underlyingType == typeof(uint)) return IsDefinedPrimitive(rt, *(uint*)&value); - if (underlyingType == typeof(long)) return IsDefinedPrimitive(rt, *(long*)&value); - if (underlyingType == typeof(ulong)) return IsDefinedPrimitive(rt, *(ulong*)&value); + if (underlyingType == typeof(sbyte)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(byte)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(short)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(ushort)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(int)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(uint)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(long)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(ulong)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return IsDefinedPrimitive(rt, *(nint*)&value); - if (underlyingType == typeof(nuint)) return IsDefinedPrimitive(rt, *(nuint*)&value); - if (underlyingType == typeof(float)) return IsDefinedPrimitive(rt, *(float*)&value); - if (underlyingType == typeof(double)) return IsDefinedPrimitive(rt, *(double*)&value); - if (underlyingType == typeof(char)) return IsDefinedPrimitive(rt, *(char*)&value); + if (underlyingType == typeof(nint)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(nuint)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(float)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(double)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(char)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); #endif throw CreateUnknownEnumTypeException(); @@ -1753,38 +1753,38 @@ public static unsafe bool TryFormat(TEnum value, Span destination, // be necessary for semantics inside of TryFormatPrimitiveNonDefault, so we can just do it here instead. if (format.IsEmpty) { - if (underlyingType == typeof(int)) return TryFormatPrimitiveDefault(rt, *(int*)&value, destination, out charsWritten); - if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault(rt, *(uint*)&value, destination, out charsWritten); - if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault(rt, *(long*)&value, destination, out charsWritten); - if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault(rt, *(ulong*)&value, destination, out charsWritten); - if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault(rt, *(byte*)&value, destination, out charsWritten); - if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault(rt, *(sbyte*)&value, destination, out charsWritten); - if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault(rt, *(short*)&value, destination, out charsWritten); - if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault(rt, *(ushort*)&value, destination, out charsWritten); + if (underlyingType == typeof(int)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, *(nint*)&value, destination, out charsWritten); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, *(nuint*)&value, destination, out charsWritten); - if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, *(float*)&value, destination, out charsWritten); - if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, *(double*)&value, destination, out charsWritten); - if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, *(char*)&value, destination, out charsWritten); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); #endif } else { - if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, *(int*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, *(uint*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, *(long*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, *(ulong*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, *(byte*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, *(sbyte*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, *(short*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, *(ushort*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, *(nint*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, *(nuint*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, *(float*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, *(double*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, *(char*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); #endif } @@ -1812,38 +1812,38 @@ internal static unsafe bool TryFormatUnconstrained(TEnum value, Span(value), destination, out charsWritten); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, *(nint*)&value, destination, out charsWritten); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, *(nuint*)&value, destination, out charsWritten); - if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, *(float*)&value, destination, out charsWritten); - if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, *(double*)&value, destination, out charsWritten); - if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, *(char*)&value, destination, out charsWritten); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); #endif } else { - if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, *(int*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, *(uint*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, *(long*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, *(ulong*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, *(byte*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, *(sbyte*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, *(short*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, *(ushort*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, *(nint*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, *(nuint*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, *(float*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, *(double*)&value, destination, out charsWritten, format); - if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, *(char*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); #endif } From df296f4ea3a6d8792e257464cce216877d9f1b45 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 13:20:35 -0800 Subject: [PATCH 06/13] Ensure BitCast resolves the right generic type for toTypeHnd --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 1f82084d7a9e1..3a2273232afbc 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4007,7 +4007,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, assert(sig->sigInst.methInstCount == 2); CORINFO_CLASS_HANDLE fromTypeHnd = sig->sigInst.methInst[0]; - CORINFO_CLASS_HANDLE toTypeHnd = sig->sigInst.methInst[0]; + CORINFO_CLASS_HANDLE toTypeHnd = sig->sigInst.methInst[1]; if (fromTypeHnd == toTypeHnd) { From aae3cfdf0a08203b5978a2ad43104b71b29443f5 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 13:56:40 -0800 Subject: [PATCH 07/13] Use Unsafe.BitCast in places using the `*(TTo*)&tfrom` pattern --- .../System/Data/ProviderBase/DbBuffer.cs | 4 +- .../src/System/Data/ProviderBase/DbBuffer.cs | 4 +- .../Parser/ValidateParser.cs | 18 +++---- .../src/System/Decimal.DecCalc.cs | 4 +- .../IndexOfAnyValues/IndexOfAny4Values.cs | 11 ++--- .../IndexOfAnyValues/IndexOfAny5Values.cs | 13 +++-- .../System.Private.CoreLib/src/System/Span.cs | 2 +- .../src/System/SpanHelpers.Packed.cs | 2 +- .../src/System/SpanHelpers.T.cs | 18 +++---- .../src/System/Threading/Volatile.cs | 4 +- .../src/System/Xml/XmlConverter.cs | 6 +-- .../Internal/Utilities/BlobUtilities.cs | 4 +- .../Converters/Value/EnumConverter.cs | 48 +++++++++---------- 13 files changed, 64 insertions(+), 74 deletions(-) diff --git a/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbBuffer.cs b/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbBuffer.cs index 4c9bf45e2d9bd..9ef1e0f7b9099 100644 --- a/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbBuffer.cs +++ b/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbBuffer.cs @@ -365,7 +365,7 @@ internal IntPtr ReadIntPtr(int offset) internal unsafe float ReadSingle(int offset) { int value = ReadInt32(offset); - return *(float*)&value; + return BitConverter.Int32BitsToSingle(value); } protected override bool ReleaseHandle() @@ -636,7 +636,7 @@ internal void WriteIntPtr(int offset, IntPtr value) internal unsafe void WriteSingle(int offset, float value) { - WriteInt32(offset, *(int*)&value); + WriteInt32(offset, BitConverter.SingleToInt32Bits(value)); } internal void ZeroMemory() diff --git a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbBuffer.cs b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbBuffer.cs index dac3f186abc15..ef399eff36208 100644 --- a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbBuffer.cs +++ b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbBuffer.cs @@ -344,7 +344,7 @@ internal IntPtr ReadIntPtr(int offset) internal unsafe float ReadSingle(int offset) { int value = ReadInt32(offset); - return *(float*)&value; + return BitConverter.Int32BitsToSingle(value); } protected override bool ReleaseHandle() @@ -615,7 +615,7 @@ internal void WriteIntPtr(int offset, IntPtr value) internal unsafe void WriteSingle(int offset, float value) { - WriteInt32(offset, *(int*)&value); + WriteInt32(offset, BitConverter.SingleToInt32Bits(value)); } internal Guid ReadGuid(int offset) diff --git a/src/libraries/System.Memory/tests/ParsersAndFormatters/Parser/ValidateParser.cs b/src/libraries/System.Memory/tests/ParsersAndFormatters/Parser/ValidateParser.cs index 6ecd116b05e04..aae401f07b711 100644 --- a/src/libraries/System.Memory/tests/ParsersAndFormatters/Parser/ValidateParser.cs +++ b/src/libraries/System.Memory/tests/ParsersAndFormatters/Parser/ValidateParser.cs @@ -106,13 +106,10 @@ private static bool IsParsedValueEqual(T expected, T actual) double expectedDouble = (double)(object)expected; double actualDouble = (double)(object)actual; - unsafe - { - if (*((ulong*)&expectedDouble) != *((ulong*)&actualDouble)) - return false; + if (BitConverter.DoubleToUInt64Bits(expectedDouble) != BitConverter.DoubleToUInt64Bits(actualDouble)) + return false; - return true; - } + return true; } // Parsed floating points are constructed, not computed. Thus, we can do the exact compare. @@ -121,13 +118,10 @@ private static bool IsParsedValueEqual(T expected, T actual) float expectedSingle = (float)(object)expected; float actualSingle = (float)(object)actual; - unsafe - { - if (*((uint*)&expectedSingle) != *((uint*)&actualSingle)) - return false; + if (BitConverter.SingleToUInt32Bits(expectedSingle) != BitConverter.SingleToUInt32Bits(actualSingle)) + return false; - return true; - } + return true; } return expected.Equals(actual); diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs index 03d4943b493c4..bd9a70b6edf41 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs @@ -160,7 +160,7 @@ private static unsafe uint GetExponent(float f) // ULONG sign:1; // } SNGSTRUCT; - return (byte)(*(uint*)&f >> 23); + return (byte)(BitConverter.SingleToUInt32Bits(f) >> 23); } private static unsafe uint GetExponent(double d) @@ -171,7 +171,7 @@ private static unsafe uint GetExponent(double d) // DWORDLONG signexp:12; // } DBLSTRUCT; - return (uint)(*(ulong*)&d >> 52) & 0x7FFu; + return (uint)(BitConverter.DoubleToUInt64Bits(d) >> 52) & 0x7FFu; } private static ulong UInt32x32To64(uint a, uint b) diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs index c23839310933c..0746c401ea3eb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs @@ -25,16 +25,15 @@ public IndexOfAny4Values(ReadOnlySpan values) internal override unsafe T[] GetValues() { - TImpl e0 = _e0, e1 = _e1, e2 = _e2, e3 = _e3; - return new[] { *(T*)&e0, *(T*)&e1, *(T*)&e2, *(T*)&e3 }; + return new[] { Unsafe.BitCast(_e0), Unsafe.BitCast(_e1), Unsafe.BitCast(_e2), Unsafe.BitCast(_e3) }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override unsafe bool ContainsCore(T value) => - *(TImpl*)&value == _e0 || - *(TImpl*)&value == _e1 || - *(TImpl*)&value == _e2 || - *(TImpl*)&value == _e3; + Unsafe.BitCast(value) == _e0 || + Unsafe.BitCast(value) == _e1 || + Unsafe.BitCast(value) == _e2 || + Unsafe.BitCast(value) == _e3; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs index 55660a22b99f1..75d182a6806c7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs @@ -25,17 +25,16 @@ public IndexOfAny5Values(ReadOnlySpan values) internal override unsafe T[] GetValues() { - TImpl e0 = _e0, e1 = _e1, e2 = _e2, e3 = _e3, e4 = _e4; - return new[] { *(T*)&e0, *(T*)&e1, *(T*)&e2, *(T*)&e3, *(T*)&e4 }; + return new[] { Unsafe.BitCast(_e0), Unsafe.BitCast(_e1), Unsafe.BitCast(_e2), Unsafe.BitCast(_e3), Unsafe.BitCast(_e4) }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override unsafe bool ContainsCore(T value) => - *(TImpl*)&value == _e0 || - *(TImpl*)&value == _e1 || - *(TImpl*)&value == _e2 || - *(TImpl*)&value == _e3 || - *(TImpl*)&value == _e4; + Unsafe.BitCast(value) == _e0 || + Unsafe.BitCast(value) == _e1 || + Unsafe.BitCast(value) == _e2 || + Unsafe.BitCast(value) == _e3 || + Unsafe.BitCast(value) == _e4; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => diff --git a/src/libraries/System.Private.CoreLib/src/System/Span.cs b/src/libraries/System.Private.CoreLib/src/System/Span.cs index 26e32ffb4cfd0..74d2407ff3003 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Span.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Span.cs @@ -305,7 +305,7 @@ public unsafe void Fill(T value) // The runtime eventually calls memset, which can efficiently support large buffers. // We don't need to check IsReferenceOrContainsReferences because no references // can ever be stored in types this small. - Unsafe.InitBlockUnaligned(ref Unsafe.As(ref _reference), *(byte*)&value, (uint)_length); + Unsafe.InitBlockUnaligned(ref Unsafe.As(ref _reference), Unsafe.BitCast(value), (uint)_length); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs index 9e8272671dd69..44f9778a9a3ee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs @@ -30,7 +30,7 @@ public static unsafe bool CanUsePackedIndexOf(T value) Debug.Assert(RuntimeHelpers.IsBitwiseEquatable()); Debug.Assert(sizeof(T) == sizeof(ushort)); - return *(ushort*)&value - 1u < 254u; + return Unsafe.BitCast(value) - 1u < 254u; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs index 11c79ef0b76af..7b78bde92f3fb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs @@ -1309,7 +1309,7 @@ internal static unsafe bool ContainsValueType(ref T searchSpace, T value, int { if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value)) { - return PackedSpanHelpers.Contains(ref Unsafe.As(ref searchSpace), *(short*)&value, length); + return PackedSpanHelpers.Contains(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value), length); } return NonPackedContainsValueType(ref searchSpace, value, length); @@ -1456,8 +1456,8 @@ private static unsafe int IndexOfValueType(ref TValue searchSp if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value)) { return typeof(TNegator) == typeof(DontNegate) - ? PackedSpanHelpers.IndexOf(ref Unsafe.As(ref searchSpace), *(char*)&value, length) - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), *(char*)&value, length); + ? PackedSpanHelpers.IndexOf(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value), length) + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value), length); } return NonPackedIndexOfValueType(ref searchSpace, value, length); @@ -1613,8 +1613,8 @@ private static unsafe int IndexOfAnyValueType(ref TValue searc if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1)) { return typeof(TNegator) == typeof(DontNegate) - ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, length) - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, length); + ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), length) + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), length); } return NonPackedIndexOfAnyValueType(ref searchSpace, value0, value1, length); @@ -1790,8 +1790,8 @@ private static unsafe int IndexOfAnyValueType(ref TValue searc if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1) && PackedSpanHelpers.CanUsePackedIndexOf(value2)) { return typeof(TNegator) == typeof(DontNegate) - ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, *(char*)&value2, length) - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, *(char*)&value2, length); + ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), Unsafe.BitCast(value2), length) + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), Unsafe.BitCast(value2), length); } return NonPackedIndexOfAnyValueType(ref searchSpace, value0, value1, value2, length); @@ -3093,8 +3093,8 @@ private static unsafe int IndexOfAnyInRangeUnsignedNumber(ref T sea if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(ushort) && PackedSpanHelpers.CanUsePackedIndexOf(lowInclusive) && PackedSpanHelpers.CanUsePackedIndexOf(highInclusive) && highInclusive >= lowInclusive) { ref char charSearchSpace = ref Unsafe.As(ref searchSpace); - char charLowInclusive = *(char*)&lowInclusive; - char charRange = (char)(*(char*)&highInclusive - charLowInclusive); + char charLowInclusive = Unsafe.BitCast(lowInclusive); + char charRange = (char)(Unsafe.BitCast(highInclusive) - charLowInclusive); return typeof(TNegator) == typeof(DontNegate) ? PackedSpanHelpers.IndexOfAnyInRange(ref charSearchSpace, charLowInclusive, charRange, length) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Volatile.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Volatile.cs index 80b2a21932e48..aeba3db6279a9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Volatile.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Volatile.cs @@ -47,13 +47,13 @@ public static void Write(ref byte location, byte value) => public static double Read(ref double location) { long result = Read(ref Unsafe.As(ref location)); - return *(double*)&result; + return BitConverter.Int64BitsToDouble(result); } [Intrinsic] [NonVersionable] public static void Write(ref double location, double value) => - Write(ref Unsafe.As(ref location), *(long*)&value); + Write(ref Unsafe.As(ref location), BitConverter.DoubleToInt64Bits(value)); #endregion #region Int16 diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs index 71867bf458dea..3420846d339a2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs @@ -710,15 +710,13 @@ public static int ToCharsR(long value, byte[] chars, int offset) private static unsafe bool IsNegativeZero(float value) { // Simple equals function will report that -0 is equal to +0, so compare bits instead - float negativeZero = -0e0F; - return (*(int*)&value == *(int*)&negativeZero); + return BitConverter.SingleToUInt32Bits(value) == 0x8000_0000U; } private static unsafe bool IsNegativeZero(double value) { // Simple equals function will report that -0 is equal to +0, so compare bits instead - double negativeZero = -0e0; - return (*(long*)&value == *(long*)&negativeZero); + return BitConverter.DoubleToUInt64Bits(value) == 0x8000_0000_0000_0000UL; } private static int ToInfinity(bool isNegative, byte[] buffer, int offset) diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs index 8f3a1321c284c..b29f5819cdcc4 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs @@ -20,12 +20,12 @@ public static void WriteBytes(this byte[] buffer, int start, byte value, int byt public static void WriteDouble(this byte[] buffer, int start, double value) { - WriteUInt64(buffer, start, *(ulong*)&value); + WriteUInt64(buffer, start, BitConverter.DoubleToUInt64Bits(value)); } public static void WriteSingle(this byte[] buffer, int start, float value) { - WriteUInt32(buffer, start, *(uint*)&value); + WriteUInt32(buffer, start, BitConverter.SingleToUInt32Bits(value)); } public static void WriteByte(this byte[] buffer, int start, byte value) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs index fe4779f87190f..a38c2dbee1e19 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs @@ -136,49 +136,49 @@ public override unsafe T Read(ref Utf8JsonReader reader, Type typeToConvert, Jso case TypeCode.Int32: if (reader.TryGetInt32(out int int32)) { - return *(T*)&int32; + return Unsafe.BitCast(int32); } break; case TypeCode.UInt32: if (reader.TryGetUInt32(out uint uint32)) { - return *(T*)&uint32; + return Unsafe.BitCast(uint32); } break; case TypeCode.UInt64: if (reader.TryGetUInt64(out ulong uint64)) { - return *(T*)&uint64; + return Unsafe.BitCast(uint64) } break; case TypeCode.Int64: if (reader.TryGetInt64(out long int64)) { - return *(T*)&int64; + return Unsafe.BitCast(int64); } break; case TypeCode.SByte: if (reader.TryGetSByte(out sbyte byte8)) { - return *(T*)&byte8; + return Unsafe.BitCast(byte8); } break; case TypeCode.Byte: if (reader.TryGetByte(out byte ubyte8)) { - return *(T*)&ubyte8; + return Unsafe.BitCast(ubyte8); } break; case TypeCode.Int16: if (reader.TryGetInt16(out short int16)) { - return *(T*)&int16; + return Unsafe.BitCast(int16); } break; case TypeCode.UInt16: if (reader.TryGetUInt16(out ushort uint16)) { - return *(T*)&uint16; + return Unsafe.BitCast(uint16); } break; } @@ -235,28 +235,28 @@ public override unsafe void Write(Utf8JsonWriter writer, T value, JsonSerializer switch (s_enumTypeCode) { case TypeCode.Int32: - writer.WriteNumberValue(*(int*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; case TypeCode.UInt32: - writer.WriteNumberValue(*(uint*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; case TypeCode.UInt64: - writer.WriteNumberValue(*(ulong*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; case TypeCode.Int64: - writer.WriteNumberValue(*(long*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; case TypeCode.Int16: - writer.WriteNumberValue(*(short*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; case TypeCode.UInt16: - writer.WriteNumberValue(*(ushort*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; case TypeCode.Byte: - writer.WriteNumberValue(*(byte*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; case TypeCode.SByte: - writer.WriteNumberValue(*(sbyte*)&value); + writer.WriteNumberValue(Unsafe.BitCast(value)); break; default: ThrowHelper.ThrowJsonException(); @@ -324,28 +324,28 @@ internal override unsafe void WriteAsPropertyNameCore(Utf8JsonWriter writer, T v switch (s_enumTypeCode) { case TypeCode.Int32: - writer.WritePropertyName(*(int*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; case TypeCode.UInt32: - writer.WritePropertyName(*(uint*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; case TypeCode.UInt64: - writer.WritePropertyName(*(ulong*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; case TypeCode.Int64: - writer.WritePropertyName(*(long*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; case TypeCode.Int16: - writer.WritePropertyName(*(short*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; case TypeCode.UInt16: - writer.WritePropertyName(*(ushort*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; case TypeCode.Byte: - writer.WritePropertyName(*(byte*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; case TypeCode.SByte: - writer.WritePropertyName(*(sbyte*)&value); + writer.WritePropertyName(Unsafe.BitCast(value)); break; default: ThrowHelper.ThrowJsonException(); From e7cd1470d56d642881acff866d8f97f70738aedf Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 14:07:20 -0800 Subject: [PATCH 08/13] Don't use BitCast in places the generic constraints disallows it --- .../System.Private.CoreLib/src/System/Enum.cs | 52 +++++++++---------- .../System.Private.CoreLib/src/System/Span.cs | 2 +- .../src/System/SpanHelpers.Packed.cs | 2 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index 2fc820c4ee6d1..b5cc1fadfb8d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -1812,38 +1812,38 @@ internal static unsafe bool TryFormatUnconstrained(TEnum value, Span(value), destination, out charsWritten); - if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(int)) return TryFormatPrimitiveDefault(rt, *(int*)&value, destination, out charsWritten); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault(rt, *(uint*)&value, destination, out charsWritten); + if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault(rt, *(long*)&value, destination, out charsWritten); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault(rt, *(ulong*)&value, destination, out charsWritten); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault(rt, *(byte*)&value, destination, out charsWritten); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault(rt, *(sbyte*)&value, destination, out charsWritten); + if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault(rt, *(short*)&value, destination, out charsWritten); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault(rt, *(ushort*)&value, destination, out charsWritten); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, *(nint*)&value, destination, out charsWritten); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, *(nuint*)&value, destination, out charsWritten); + if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, *(float*)&value, destination, out charsWritten); + if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, *(double*)&value, destination, out charsWritten); + if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, *(char*)&value, destination, out charsWritten); #endif } else { - if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, *(int*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, *(uint*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, *(long*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, *(ulong*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, *(byte*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, *(sbyte*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, *(short*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, *(ushort*)&value, destination, out charsWritten, format); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, *(nint*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, *(nuint*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, *(float*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, *(double*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, *(char*)&value, destination, out charsWritten, format); #endif } diff --git a/src/libraries/System.Private.CoreLib/src/System/Span.cs b/src/libraries/System.Private.CoreLib/src/System/Span.cs index 74d2407ff3003..26e32ffb4cfd0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Span.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Span.cs @@ -305,7 +305,7 @@ public unsafe void Fill(T value) // The runtime eventually calls memset, which can efficiently support large buffers. // We don't need to check IsReferenceOrContainsReferences because no references // can ever be stored in types this small. - Unsafe.InitBlockUnaligned(ref Unsafe.As(ref _reference), Unsafe.BitCast(value), (uint)_length); + Unsafe.InitBlockUnaligned(ref Unsafe.As(ref _reference), *(byte*)&value, (uint)_length); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs index 44f9778a9a3ee..9e8272671dd69 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs @@ -30,7 +30,7 @@ public static unsafe bool CanUsePackedIndexOf(T value) Debug.Assert(RuntimeHelpers.IsBitwiseEquatable()); Debug.Assert(sizeof(T) == sizeof(ushort)); - return Unsafe.BitCast(value) - 1u < 254u; + return *(ushort*)&value - 1u < 254u; } [MethodImpl(MethodImplOptions.AggressiveInlining)] From e2875ddd3e1f1cf0f8131d626aaf8c5dcb8a1386 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 2 Mar 2023 14:13:44 -0800 Subject: [PATCH 09/13] Missing semicolon --- .../Text/Json/Serialization/Converters/Value/EnumConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs index a38c2dbee1e19..46001c627826f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs @@ -148,7 +148,7 @@ public override unsafe T Read(ref Utf8JsonReader reader, Type typeToConvert, Jso case TypeCode.UInt64: if (reader.TryGetUInt64(out ulong uint64)) { - return Unsafe.BitCast(uint64) + return Unsafe.BitCast(uint64); } break; case TypeCode.Int64: From b34275665cb052eb3d629ebc3c3ceb4442461a8c Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 3 Mar 2023 08:06:40 -0800 Subject: [PATCH 10/13] Don't use Unsafe.BitCast where it introduces additional generic instantiations --- .../System.Private.CoreLib/src/System/Enum.cs | 104 +++++++++--------- .../IndexOfAnyValues/IndexOfAny4Values.cs | 11 +- .../IndexOfAnyValues/IndexOfAny5Values.cs | 13 ++- .../src/System/SpanHelpers.T.cs | 18 +-- .../Converters/Value/EnumConverter.cs | 48 ++++---- 5 files changed, 98 insertions(+), 96 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index b5cc1fadfb8d0..534823f28956f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -46,20 +46,20 @@ public abstract partial class Enum : ValueType, IComparable, ISpanFormattable, I RuntimeType rt = (RuntimeType)typeof(TEnum); Type underlyingType = typeof(TEnum).GetEnumUnderlyingType(); - if (underlyingType == typeof(sbyte)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(byte)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(short)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(ushort)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(int)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(uint)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(long)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(ulong)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(sbyte)) return GetNameInlined(GetEnumInfo(rt), *(sbyte*)&value); + if (underlyingType == typeof(byte)) return GetNameInlined(GetEnumInfo(rt), *(byte*)&value); + if (underlyingType == typeof(short)) return GetNameInlined(GetEnumInfo(rt), *(short*)&value); + if (underlyingType == typeof(ushort)) return GetNameInlined(GetEnumInfo(rt), *(ushort*)&value); + if (underlyingType == typeof(int)) return GetNameInlined(GetEnumInfo(rt), *(int*)&value); + if (underlyingType == typeof(uint)) return GetNameInlined(GetEnumInfo(rt), *(uint*)&value); + if (underlyingType == typeof(long)) return GetNameInlined(GetEnumInfo(rt), *(long*)&value); + if (underlyingType == typeof(ulong)) return GetNameInlined(GetEnumInfo(rt), *(ulong*)&value); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(nuint)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(float)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(double)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); - if (underlyingType == typeof(char)) return GetNameInlined(GetEnumInfo(rt), Unsafe.BitCast(value)); + if (underlyingType == typeof(nint)) return GetNameInlined(GetEnumInfo(rt), *(nint*)&value); + if (underlyingType == typeof(nuint)) return GetNameInlined(GetEnumInfo(rt), *(nuint*)&value); + if (underlyingType == typeof(float)) return GetNameInlined(GetEnumInfo(rt), *(float*)&value); + if (underlyingType == typeof(double)) return GetNameInlined(GetEnumInfo(rt), *(double*)&value); + if (underlyingType == typeof(char)) return GetNameInlined(GetEnumInfo(rt), *(char*)&value); #endif throw CreateUnknownEnumTypeException(); } @@ -473,20 +473,20 @@ public static unsafe bool IsDefined(TEnum value) where TEnum : struct, En RuntimeType rt = (RuntimeType)typeof(TEnum); Type underlyingType = typeof(TEnum).GetEnumUnderlyingType(); - if (underlyingType == typeof(sbyte)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(byte)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(short)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(ushort)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(int)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(uint)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(long)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(ulong)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(sbyte)) return IsDefinedPrimitive(rt, *(sbyte*)&value); + if (underlyingType == typeof(byte)) return IsDefinedPrimitive(rt, *(byte*)&value); + if (underlyingType == typeof(short)) return IsDefinedPrimitive(rt, *(short*)&value); + if (underlyingType == typeof(ushort)) return IsDefinedPrimitive(rt, *(ushort*)&value); + if (underlyingType == typeof(int)) return IsDefinedPrimitive(rt, *(int*)&value); + if (underlyingType == typeof(uint)) return IsDefinedPrimitive(rt, *(uint*)&value); + if (underlyingType == typeof(long)) return IsDefinedPrimitive(rt, *(long*)&value); + if (underlyingType == typeof(ulong)) return IsDefinedPrimitive(rt, *(ulong*)&value); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(nuint)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(float)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(double)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); - if (underlyingType == typeof(char)) return IsDefinedPrimitive(rt, Unsafe.BitCast(value)); + if (underlyingType == typeof(nint)) return IsDefinedPrimitive(rt, *(nint*)&value); + if (underlyingType == typeof(nuint)) return IsDefinedPrimitive(rt, *(nuint*)&value); + if (underlyingType == typeof(float)) return IsDefinedPrimitive(rt, *(float*)&value); + if (underlyingType == typeof(double)) return IsDefinedPrimitive(rt, *(double*)&value); + if (underlyingType == typeof(char)) return IsDefinedPrimitive(rt, *(char*)&value); #endif throw CreateUnknownEnumTypeException(); @@ -1753,38 +1753,38 @@ public static unsafe bool TryFormat(TEnum value, Span destination, // be necessary for semantics inside of TryFormatPrimitiveNonDefault, so we can just do it here instead. if (format.IsEmpty) { - if (underlyingType == typeof(int)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(int)) return TryFormatPrimitiveDefault(rt, *(int*)&value, destination, out charsWritten); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault(rt, *(uint*)&value, destination, out charsWritten); + if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault(rt, *(long*)&value, destination, out charsWritten); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault(rt, *(ulong*)&value, destination, out charsWritten); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault(rt, *(byte*)&value, destination, out charsWritten); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault(rt, *(sbyte*)&value, destination, out charsWritten); + if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault(rt, *(short*)&value, destination, out charsWritten); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault(rt, *(ushort*)&value, destination, out charsWritten); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); - if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, Unsafe.BitCast(value), destination, out charsWritten); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault(rt, *(nint*)&value, destination, out charsWritten); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault(rt, *(nuint*)&value, destination, out charsWritten); + if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault(rt, *(float*)&value, destination, out charsWritten); + if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault(rt, *(double*)&value, destination, out charsWritten); + if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault(rt, *(char*)&value, destination, out charsWritten); #endif } else { - if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault(rt, *(int*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault(rt, *(uint*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault(rt, *(long*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault(rt, *(ulong*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault(rt, *(byte*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault(rt, *(sbyte*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault(rt, *(short*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault(rt, *(ushort*)&value, destination, out charsWritten, format); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); - if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, Unsafe.BitCast(value), destination, out charsWritten, format); + if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault(rt, *(nint*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault(rt, *(nuint*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault(rt, *(float*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault(rt, *(double*)&value, destination, out charsWritten, format); + if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault(rt, *(char*)&value, destination, out charsWritten, format); #endif } diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs index 0746c401ea3eb..c23839310933c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny4Values.cs @@ -25,15 +25,16 @@ public IndexOfAny4Values(ReadOnlySpan values) internal override unsafe T[] GetValues() { - return new[] { Unsafe.BitCast(_e0), Unsafe.BitCast(_e1), Unsafe.BitCast(_e2), Unsafe.BitCast(_e3) }; + TImpl e0 = _e0, e1 = _e1, e2 = _e2, e3 = _e3; + return new[] { *(T*)&e0, *(T*)&e1, *(T*)&e2, *(T*)&e3 }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override unsafe bool ContainsCore(T value) => - Unsafe.BitCast(value) == _e0 || - Unsafe.BitCast(value) == _e1 || - Unsafe.BitCast(value) == _e2 || - Unsafe.BitCast(value) == _e3; + *(TImpl*)&value == _e0 || + *(TImpl*)&value == _e1 || + *(TImpl*)&value == _e2 || + *(TImpl*)&value == _e3; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs index 75d182a6806c7..55660a22b99f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny5Values.cs @@ -25,16 +25,17 @@ public IndexOfAny5Values(ReadOnlySpan values) internal override unsafe T[] GetValues() { - return new[] { Unsafe.BitCast(_e0), Unsafe.BitCast(_e1), Unsafe.BitCast(_e2), Unsafe.BitCast(_e3), Unsafe.BitCast(_e4) }; + TImpl e0 = _e0, e1 = _e1, e2 = _e2, e3 = _e3, e4 = _e4; + return new[] { *(T*)&e0, *(T*)&e1, *(T*)&e2, *(T*)&e3, *(T*)&e4 }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override unsafe bool ContainsCore(T value) => - Unsafe.BitCast(value) == _e0 || - Unsafe.BitCast(value) == _e1 || - Unsafe.BitCast(value) == _e2 || - Unsafe.BitCast(value) == _e3 || - Unsafe.BitCast(value) == _e4; + *(TImpl*)&value == _e0 || + *(TImpl*)&value == _e1 || + *(TImpl*)&value == _e2 || + *(TImpl*)&value == _e3 || + *(TImpl*)&value == _e4; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs index 7b78bde92f3fb..11c79ef0b76af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs @@ -1309,7 +1309,7 @@ internal static unsafe bool ContainsValueType(ref T searchSpace, T value, int { if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value)) { - return PackedSpanHelpers.Contains(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value), length); + return PackedSpanHelpers.Contains(ref Unsafe.As(ref searchSpace), *(short*)&value, length); } return NonPackedContainsValueType(ref searchSpace, value, length); @@ -1456,8 +1456,8 @@ private static unsafe int IndexOfValueType(ref TValue searchSp if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value)) { return typeof(TNegator) == typeof(DontNegate) - ? PackedSpanHelpers.IndexOf(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value), length) - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value), length); + ? PackedSpanHelpers.IndexOf(ref Unsafe.As(ref searchSpace), *(char*)&value, length) + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), *(char*)&value, length); } return NonPackedIndexOfValueType(ref searchSpace, value, length); @@ -1613,8 +1613,8 @@ private static unsafe int IndexOfAnyValueType(ref TValue searc if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1)) { return typeof(TNegator) == typeof(DontNegate) - ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), length) - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), length); + ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, length) + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, length); } return NonPackedIndexOfAnyValueType(ref searchSpace, value0, value1, length); @@ -1790,8 +1790,8 @@ private static unsafe int IndexOfAnyValueType(ref TValue searc if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1) && PackedSpanHelpers.CanUsePackedIndexOf(value2)) { return typeof(TNegator) == typeof(DontNegate) - ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), Unsafe.BitCast(value2), length) - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), Unsafe.BitCast(value0), Unsafe.BitCast(value1), Unsafe.BitCast(value2), length); + ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, *(char*)&value2, length) + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, *(char*)&value2, length); } return NonPackedIndexOfAnyValueType(ref searchSpace, value0, value1, value2, length); @@ -3093,8 +3093,8 @@ private static unsafe int IndexOfAnyInRangeUnsignedNumber(ref T sea if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(ushort) && PackedSpanHelpers.CanUsePackedIndexOf(lowInclusive) && PackedSpanHelpers.CanUsePackedIndexOf(highInclusive) && highInclusive >= lowInclusive) { ref char charSearchSpace = ref Unsafe.As(ref searchSpace); - char charLowInclusive = Unsafe.BitCast(lowInclusive); - char charRange = (char)(Unsafe.BitCast(highInclusive) - charLowInclusive); + char charLowInclusive = *(char*)&lowInclusive; + char charRange = (char)(*(char*)&highInclusive - charLowInclusive); return typeof(TNegator) == typeof(DontNegate) ? PackedSpanHelpers.IndexOfAnyInRange(ref charSearchSpace, charLowInclusive, charRange, length) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs index 46001c627826f..fe4779f87190f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs @@ -136,49 +136,49 @@ public override unsafe T Read(ref Utf8JsonReader reader, Type typeToConvert, Jso case TypeCode.Int32: if (reader.TryGetInt32(out int int32)) { - return Unsafe.BitCast(int32); + return *(T*)&int32; } break; case TypeCode.UInt32: if (reader.TryGetUInt32(out uint uint32)) { - return Unsafe.BitCast(uint32); + return *(T*)&uint32; } break; case TypeCode.UInt64: if (reader.TryGetUInt64(out ulong uint64)) { - return Unsafe.BitCast(uint64); + return *(T*)&uint64; } break; case TypeCode.Int64: if (reader.TryGetInt64(out long int64)) { - return Unsafe.BitCast(int64); + return *(T*)&int64; } break; case TypeCode.SByte: if (reader.TryGetSByte(out sbyte byte8)) { - return Unsafe.BitCast(byte8); + return *(T*)&byte8; } break; case TypeCode.Byte: if (reader.TryGetByte(out byte ubyte8)) { - return Unsafe.BitCast(ubyte8); + return *(T*)&ubyte8; } break; case TypeCode.Int16: if (reader.TryGetInt16(out short int16)) { - return Unsafe.BitCast(int16); + return *(T*)&int16; } break; case TypeCode.UInt16: if (reader.TryGetUInt16(out ushort uint16)) { - return Unsafe.BitCast(uint16); + return *(T*)&uint16; } break; } @@ -235,28 +235,28 @@ public override unsafe void Write(Utf8JsonWriter writer, T value, JsonSerializer switch (s_enumTypeCode) { case TypeCode.Int32: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(int*)&value); break; case TypeCode.UInt32: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(uint*)&value); break; case TypeCode.UInt64: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(ulong*)&value); break; case TypeCode.Int64: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(long*)&value); break; case TypeCode.Int16: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(short*)&value); break; case TypeCode.UInt16: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(ushort*)&value); break; case TypeCode.Byte: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(byte*)&value); break; case TypeCode.SByte: - writer.WriteNumberValue(Unsafe.BitCast(value)); + writer.WriteNumberValue(*(sbyte*)&value); break; default: ThrowHelper.ThrowJsonException(); @@ -324,28 +324,28 @@ internal override unsafe void WriteAsPropertyNameCore(Utf8JsonWriter writer, T v switch (s_enumTypeCode) { case TypeCode.Int32: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(int*)&value); break; case TypeCode.UInt32: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(uint*)&value); break; case TypeCode.UInt64: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(ulong*)&value); break; case TypeCode.Int64: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(long*)&value); break; case TypeCode.Int16: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(short*)&value); break; case TypeCode.UInt16: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(ushort*)&value); break; case TypeCode.Byte: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(byte*)&value); break; case TypeCode.SByte: - writer.WritePropertyName(Unsafe.BitCast(value)); + writer.WritePropertyName(*(sbyte*)&value); break; default: ThrowHelper.ThrowJsonException(); From bc80205e5f9dd23f93329e25524a210dc1d40446 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 3 Mar 2023 09:46:50 -0800 Subject: [PATCH 11/13] Don't regress the files that are used for both netstandard and netcoreapp --- .../System/Reflection/Internal/Utilities/BlobUtilities.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs index b29f5819cdcc4..843e95e616d34 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BlobUtilities.cs @@ -20,12 +20,20 @@ public static void WriteBytes(this byte[] buffer, int start, byte value, int byt public static void WriteDouble(this byte[] buffer, int start, double value) { +#if NETCOREAPP WriteUInt64(buffer, start, BitConverter.DoubleToUInt64Bits(value)); +#else + WriteUInt64(buffer, start, *(ulong*)&value); +#endif } public static void WriteSingle(this byte[] buffer, int start, float value) { +#if NETCOREAPP WriteUInt32(buffer, start, BitConverter.SingleToUInt32Bits(value)); +#else + WriteUInt32(buffer, start, *(uint*)&value); +#endif } public static void WriteByte(this byte[] buffer, int start, byte value) From 29afa956672fc640fc60c9554ee0e5377e4cfa49 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 8 Mar 2023 06:03:56 -0800 Subject: [PATCH 12/13] Responding to PR feedback --- src/coreclr/jit/importercalls.cpp | 4 ++- .../System/Runtime/CompilerServices/Unsafe.cs | 2 +- .../tests/UnsafeTests.cs | 33 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 3a2273232afbc..7b437bbc52af4 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4018,6 +4018,9 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, unsigned fromSize = info.compCompHnd->getClassSize(fromTypeHnd); unsigned toSize = info.compCompHnd->getClassSize(toTypeHnd); + // Runtime requires all types to be at least 1-byte + assert((fromSize != 0) && (toSize != 0)); + if (fromSize != toSize) { // Fallback to the software implementation to throw when sizes don't match @@ -4132,7 +4135,6 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, { return gtNewBitCastNode(toType, op1); } - break; } // Handle bitcasting for same sized integrals, such as `int` to `uint` diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs index a82d974884aaf..ef7a6fc4e84fd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs @@ -239,7 +239,7 @@ public static bool AreSame([AllowNull] ref T left, [AllowNull] ref T right) } /// - /// Reinterprets the given reference as a reference to a value of type . + /// Reinterprets the given value of type as a value of type . /// /// The size of and are not the same. [Intrinsic] diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index c0171353f3096..ebc87cb26cc4c 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1084,15 +1084,23 @@ public static unsafe void NullRef() [Fact] public static unsafe void BitCast() { + // Conversion between differently sized types should fail + Assert.Throws(() => Unsafe.BitCast(5)); Assert.Throws(() => Unsafe.BitCast(5)); + // Conversion between floating-point and same sized integral should succeed + Assert.Equal(0x8000_0000u, Unsafe.BitCast(-0.0f)); Assert.Equal(float.PositiveInfinity, Unsafe.BitCast(0x7F80_0000u)); + // Conversion between same sized integers should succeed + Assert.Equal(int.MinValue, Unsafe.BitCast(0x8000_0000u)); Assert.Equal(0x8000_0000u, Unsafe.BitCast(int.MinValue)); + // Conversion from runtime SIMD type to a custom struct should succeed + Vector4 vector4a = new Vector4(1.0f, 2.0f, 3.0f, 4.0f); Single4 single4a = Unsafe.BitCast(vector4a); @@ -1101,6 +1109,8 @@ public static unsafe void BitCast() Assert.Equal(3.0f, single4a.Z); Assert.Equal(4.0f, single4a.W); + // Conversion from custom struct to a runtime SIMD type should succeed + Single4 single4b = new Single4 { X = -1.0f, Y = -2.0f, Z = -3.0f, W = -4.0f }; Vector4 vector4b = Unsafe.BitCast(single4b); @@ -1108,6 +1118,21 @@ public static unsafe void BitCast() Assert.Equal(-2.0f, vector4b.Y); Assert.Equal(-3.0f, vector4b.Z); Assert.Equal(-4.0f, vector4b.W); + + // Runtime requires that all types be at least 1-byte, so empty to empty should succeed + + EmptyA empty1 = new EmptyA(); + EmptyB empty2 = Unsafe.BitCast(empty1); + + // ..., likewise, empty to/from byte should succeed + + byte empty3 = Unsafe.BitCast(empty1); + EmptyA empty4 = Unsafe.BitCast(1); + + // ..., however, empty to/from a larger type should fail + + Assert.Throws(() => Unsafe.BitCast(5)); + Assert.Throws(() => Unsafe.BitCast(emptyA)); } } @@ -1193,4 +1218,12 @@ public struct Single4 public float Z; public float W; } + + public struct EmptyA + { + } + + public struct EmptyB + { + } } From 22012d8ef04c6f93176747d7a503008ce302a8a7 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 8 Mar 2023 06:27:26 -0800 Subject: [PATCH 13/13] Fix a typo in the bitcast tests --- .../System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index ebc87cb26cc4c..8d596482766a4 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1132,7 +1132,7 @@ public static unsafe void BitCast() // ..., however, empty to/from a larger type should fail Assert.Throws(() => Unsafe.BitCast(5)); - Assert.Throws(() => Unsafe.BitCast(emptyA)); + Assert.Throws(() => Unsafe.BitCast(empty1)); } }