diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 3c4f41c81e1725..3137b834d75bd0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -4,8 +4,9 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Internal.Runtime.CompilerServices; namespace System @@ -225,8 +226,8 @@ private static unsafe void Copy(Array sourceArray, int sourceIndex, Array destin nuint elementSize = (nuint)pMT->ComponentSize; nuint byteCount = (uint)length * elementSize; - ref byte src = ref Unsafe.AddByteOffset(ref sourceArray.GetRawArrayData(), (uint)sourceIndex * elementSize); - ref byte dst = ref Unsafe.AddByteOffset(ref destinationArray.GetRawArrayData(), (uint)destinationIndex * elementSize); + ref byte src = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), (uint)sourceIndex * elementSize); + ref byte dst = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(destinationArray), (uint)destinationIndex * elementSize); if (pMT->ContainsGCPointers) Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount); @@ -278,7 +279,7 @@ public static unsafe void Clear(Array array) MethodTable* pMT = RuntimeHelpers.GetMethodTable(array); nuint totalByteLength = pMT->ComponentSize * array.NativeLength; - ref byte pStart = ref array.GetRawArrayData(); + ref byte pStart = ref MemoryMarshal.GetArrayDataReference(array); if (!pMT->ContainsGCPointers) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index b4d95defcaa5bb..6e3d8b57f504d3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -227,11 +227,6 @@ internal static unsafe nuint GetRawObjectDataSize(object obj) return rawSize; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe ref byte GetRawArrayData(this Array array) => - // See comment on RawArrayData for details - ref Unsafe.AddByteOffset(ref Unsafe.As(array).Data, (nuint)GetMethodTable(array)->BaseSize - (nuint)(2 * sizeof(IntPtr))); - internal static unsafe ushort GetElementSize(this Array array) { Debug.Assert(ObjectHasComponentSize(array)); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs index 021a0a2f52be17..ba247054e4b46e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs @@ -7,7 +7,7 @@ namespace System.Runtime.InteropServices { - public static partial class MemoryMarshal + public static unsafe partial class MemoryMarshal { /// /// Returns a reference to the 0th element of . If the array is empty, returns a reference to where the 0th element @@ -20,7 +20,29 @@ public static partial class MemoryMarshal /// [Intrinsic] [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetArrayDataReference(T[] array) => ref Unsafe.As(ref Unsafe.As(array).Data); + + /// + /// Returns a reference to the 0th element of . If the array is empty, returns a reference to where the 0th element + /// would have been stored. Such a reference may be used for pinning but must never be dereferenced. + /// + /// is . + /// + /// The caller must manually reinterpret the returned ref byte as a ref to the array's underlying elemental type, + /// perhaps utilizing an API such as System.Runtime.CompilerServices.Unsafe.As to assist with the reinterpretation. + /// This technique does not perform array variance checks. The caller must manually perform any array variance checks + /// if the caller wishes to write to the returned reference. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref byte GetArrayDataReference(Array array) + { + // If needed, we can save one or two instructions per call by marking this method as intrinsic and asking the JIT + // to special-case arrays of known type and dimension. + + // See comment on RawArrayData (in RuntimeHelpers.CoreCLR.cs) for details + return ref Unsafe.AddByteOffset(ref Unsafe.As(array).Data, (nuint)RuntimeHelpers.GetMethodTable(array)->BaseSize - (nuint)(2 * sizeof(IntPtr))); + } } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs index fc1d74bf326366..fee30121c44f21 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs @@ -17,6 +17,11 @@ public static MethodIL EmitIL(MethodDesc method) Debug.Assert(((MetadataType)method.OwningType).Name == "MemoryMarshal"); string methodName = method.Name; + if (method.Instantiation.Length != 1) + { + return null; // we only handle the generic method GetArrayDataReference(T[]) + } + if (methodName == "GetArrayDataReference") { ILEmitter emit = new ILEmitter(); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 5fe53b824e83a9..73ce65fd2d7642 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -729,7 +729,6 @@ DEFINE_METHOD(RUNTIME_HELPERS, IS_REFERENCE_OR_CONTAINS_REFERENCES, IsRefer DEFINE_METHOD(RUNTIME_HELPERS, IS_BITWISE_EQUATABLE, IsBitwiseEquatable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_METHOD_TABLE, GetMethodTable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_DATA, GetRawData, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_ARRAY_DATA, GetRawArrayData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_UNINITIALIZED_OBJECT, GetUninitializedObject, SM_Type_RetObj) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_EQUALS, EnumEquals, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_COMPARE_TO, EnumCompareTo, NoSig) @@ -762,7 +761,8 @@ DEFINE_METHOD(UNSAFE, PTR_WRITE_UNALIGNED, WriteUnaligned, GM_P DEFINE_METHOD(UNSAFE, SKIPINIT, SkipInit, GM_RefT_RetVoid) DEFINE_CLASS(MEMORY_MARSHAL, Interop, MemoryMarshal) -DEFINE_METHOD(MEMORY_MARSHAL, GET_ARRAY_DATA_REFERENCE, GetArrayDataReference, NoSig) +DEFINE_METHOD(MEMORY_MARSHAL, GET_ARRAY_DATA_REFERENCE_SZARRAY, GetArrayDataReference, GM_ArrT_RetRefT) +DEFINE_METHOD(MEMORY_MARSHAL, GET_ARRAY_DATA_REFERENCE_MDARRAY, GetArrayDataReference, SM_Array_RetRefByte) DEFINE_CLASS(INTERLOCKED, Threading, Interlocked) DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_T, CompareExchange, GM_RefT_T_T_RetT) diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index 3b45dc673c2332..5c7517b28847c1 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -3663,7 +3663,7 @@ void ILArrayWithOffsetMarshaler::EmitConvertSpaceAndContentsCLRToNativeTemp(ILCo EmitLoadNativeValue(pslILEmit); // dest pslILEmit->EmitLDLOC(m_dwPinnedLocalNum); - pslILEmit->EmitCALL(METHOD__RUNTIME_HELPERS__GET_RAW_ARRAY_DATA, 1, 1); + pslILEmit->EmitCALL(METHOD__MEMORY_MARSHAL__GET_ARRAY_DATA_REFERENCE_MDARRAY, 1, 1); pslILEmit->EmitCONV_I(); EmitLoadManagedValue(pslILEmit); @@ -3707,7 +3707,7 @@ void ILArrayWithOffsetMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* ps pslILEmit->EmitSTLOC(m_dwPinnedLocalNum); pslILEmit->EmitLDLOC(m_dwPinnedLocalNum); - pslILEmit->EmitCALL(METHOD__RUNTIME_HELPERS__GET_RAW_ARRAY_DATA, 1, 1); + pslILEmit->EmitCALL(METHOD__MEMORY_MARSHAL__GET_ARRAY_DATA_REFERENCE_MDARRAY, 1, 1); pslILEmit->EmitCONV_I(); pslILEmit->EmitLDLOC(m_dwOffsetLocalNum); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 44dfe8fe73b286..669992a80a459f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -7228,7 +7228,7 @@ bool getILIntrinsicImplementationForMemoryMarshal(MethodDesc * ftn, mdMethodDef tk = ftn->GetMemberDef(); - if (tk == CoreLibBinder::GetMethod(METHOD__MEMORY_MARSHAL__GET_ARRAY_DATA_REFERENCE)->GetMemberDef()) + if (tk == CoreLibBinder::GetMethod(METHOD__MEMORY_MARSHAL__GET_ARRAY_DATA_REFERENCE_SZARRAY)->GetMemberDef()) { mdToken tokRawSzArrayData = CoreLibBinder::GetField(FIELD__RAW_ARRAY_DATA__DATA)->GetMemberDef(); diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 3716ca9fd7b858..8c4b3f4a03e674 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -304,6 +304,9 @@ DEFINE_METASIG(GM(RefT_IntPtr_RetRefT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)) DEFINE_METASIG(GM(PtrVoid_Int_RetPtrVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, P(v) i, P(v))) DEFINE_METASIG(GM(RefT_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)), v)) +DEFINE_METASIG(GM(ArrT_RetRefT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, a(M(0)), r(M(0)))) +DEFINE_METASIG_T(SM(Array_RetRefByte, C(ARRAY), r(b))) + DEFINE_METASIG_T(SM(SafeHandle_RefBool_RetIntPtr, C(SAFE_HANDLE) r(F), I )) DEFINE_METASIG_T(SM(SafeHandle_RetVoid, C(SAFE_HANDLE), v )) diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index e90b385ca656e2..d9d629deb04e5a 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -505,6 +505,7 @@ public static partial class MemoryMarshal public static unsafe ReadOnlySpan CreateReadOnlySpanFromNullTerminated(char* value) { throw null; } public static System.Span CreateSpan(ref T reference, int length) { throw null; } public static ref T GetArrayDataReference(T[] array) { throw null; } + public static ref byte GetArrayDataReference(System.Array array) { throw null; } public static ref T GetReference(System.ReadOnlySpan span) { throw null; } public static ref T GetReference(System.Span span) { throw null; } public static T Read(System.ReadOnlySpan source) where T : struct { throw null; } diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs b/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs index 5652d275f90cc7..c5ed1414d6a99f 100644 --- a/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs +++ b/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs @@ -1,9 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Xunit; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Xunit; namespace System.SpanTests { @@ -13,18 +14,31 @@ public static partial class MemoryMarshalTests [ActiveIssue("https://github.com/dotnet/runtime/issues/36885", TestPlatforms.tvOS)] public static void GetArrayDataReference_NullInput_ThrowsNullRef() { - Assert.Throws(() => MemoryMarshal.GetArrayDataReference((object[])null)); + Assert.Throws(() => MemoryMarshal.GetArrayDataReference((object[])null)); + Assert.Throws(() => MemoryMarshal.GetArrayDataReference((Array)null)); } [Fact] public static void GetArrayDataReference_NonEmptyInput_ReturnsRefToFirstElement() { - int[] theArray = new int[] { 10, 20, 30 }; - Assert.True(Unsafe.AreSame(ref theArray[0], ref MemoryMarshal.GetArrayDataReference(theArray))); + // szarray + int[] szArray = new int[] { 10, 20, 30 }; + Assert.True(Unsafe.AreSame(ref szArray[0], ref MemoryMarshal.GetArrayDataReference(szArray))); + Assert.True(Unsafe.AreSame(ref szArray[0], ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference((Array)szArray)))); + + // mdarray, rank 2 + int[,] mdArrayRank2 = new int[3, 2]; + Assert.True(Unsafe.AreSame(ref mdArrayRank2[0, 0], ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(mdArrayRank2)))); + + // mdarray, custom bounds + // there's no baseline way to get a ref to element (10, 20, 30), so we'll deref the result of GetArrayDataReference and compare + Array mdArrayCustomBounds = Array.CreateInstance(typeof(int), new[] { 1, 2, 3 }, new int[] { 10, 20, 30 }); + mdArrayCustomBounds.SetValue(0x12345678, new[] { 10, 20, 30 }); + Assert.Equal(0x12345678, Unsafe.As(ref MemoryMarshal.GetArrayDataReference(mdArrayCustomBounds))); } [Fact] - public static unsafe void GetArrayDataReference_EmptyInput_ReturnsRefToWhereFirstElementWouldBe() + public static unsafe void GetArrayDataReference_EmptyInput_ReturnsRefToWhereFirstElementWouldBe_SzArray() { int[] theArray = new int[0]; @@ -32,6 +46,35 @@ public static unsafe void GetArrayDataReference_EmptyInput_ReturnsRefToWhereFirs Assert.True(Unsafe.AsPointer(ref theRef) != null); Assert.True(Unsafe.AreSame(ref theRef, ref MemoryMarshal.GetReference(theArray.AsSpan()))); + + ref int theMdArrayRef = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference((Array)theArray)); // szarray passed to generalized Array helper + Assert.True(Unsafe.AreSame(ref theRef, ref theMdArrayRef)); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + public static unsafe void GetArrayDataReference_EmptyInput_ReturnsRefToWhereFirstElementWouldBe_MdArray(int rank) + { + // First, compute how much distance there is between the start of the object data and the first element + // of a baseline (non-empty) array. + + int[] lowerDims = Enumerable.Range(100, rank).ToArray(); + int[] lengths = Enumerable.Range(1, rank).ToArray(); + + Array baselineArray = Array.CreateInstance(typeof(int), lengths, lowerDims); + IntPtr baselineOffset = Unsafe.ByteOffset(ref Unsafe.As(baselineArray).Data, ref MemoryMarshal.GetArrayDataReference(baselineArray)); + + // Then, perform the same calculation with an empty array of equal rank, and ensure the offsets are identical. + + lengths = new int[rank]; // = { 0, 0, 0, ... } + + Array emptyArray = Array.CreateInstance(typeof(int), lengths, lowerDims); + IntPtr emptyArrayOffset = Unsafe.ByteOffset(ref Unsafe.As(emptyArray).Data, ref MemoryMarshal.GetArrayDataReference(emptyArray)); + + Assert.Equal(baselineOffset, emptyArrayOffset); } [Fact] @@ -45,5 +88,10 @@ public static void GetArrayDataReference_IgnoresArrayVarianceChecks() Assert.True(Unsafe.AreSame(ref refObj, ref Unsafe.As(ref strArr[0]))); } + + private sealed class RawObject + { + internal byte Data; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 4879ff45deca8f..595283b1a58c30 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -2374,7 +2374,7 @@ private void InsertionSort(int lo, int hi) } private static Span UnsafeArrayAsSpan(Array array, int adjustedIndex, int length) => - new Span(ref Unsafe.As(ref array.GetRawArrayData()), array.Length).Slice(adjustedIndex, length); + new Span(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), array.Length).Slice(adjustedIndex, length); public IEnumerator GetEnumerator() { diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs index 5d9f18e91c574a..64bd6fca168e33 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs @@ -60,7 +60,7 @@ public static unsafe void BlockCopy(Array src, int srcOffset, Array dst, int dst if ((uSrcLen < uSrcOffset + uCount) || (uDstLen < uDstOffset + uCount)) throw new ArgumentException(SR.Argument_InvalidOffLen); - Memmove(ref Unsafe.AddByteOffset(ref dst.GetRawArrayData(), uDstOffset), ref Unsafe.AddByteOffset(ref src.GetRawArrayData(), uSrcOffset), uCount); + Memmove(ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(dst), uDstOffset), ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(src), uSrcOffset), uCount); } public static int ByteLength(Array array) @@ -94,7 +94,7 @@ public static byte GetByte(Array array, int index) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); } - return Unsafe.Add(ref array.GetRawArrayData(), index); + return Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index); } public static void SetByte(Array array, int index, byte value) @@ -105,7 +105,7 @@ public static void SetByte(Array array, int index, byte value) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); } - Unsafe.Add(ref array.GetRawArrayData(), index) = value; + Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index) = value; } internal static unsafe void ZeroMemory(byte* dest, nuint len) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs index b348c0b48161b7..78aa52682e417a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs @@ -134,7 +134,7 @@ public IntPtr AddrOfPinnedObject() } Debug.Assert(target is Array); - return (IntPtr)Unsafe.AsPointer(ref Unsafe.As(target).GetRawArrayData()); + return (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(target))); } return (IntPtr)Unsafe.AsPointer(ref target.GetRawData()); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 0f068648f7d7a2..15a9d86a224a96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -191,7 +191,7 @@ public static unsafe IntPtr UnsafeAddrOfPinnedArrayElement(Array arr, int index) if (arr is null) throw new ArgumentNullException(nameof(arr)); - void* pRawData = Unsafe.AsPointer(ref arr.GetRawArrayData()); + void* pRawData = Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(arr)); return (IntPtr)((byte*)pRawData + (uint)index * (nuint)arr.GetElementSize()); } diff --git a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs index 7926abd1b72187..94cb9eb7feea62 100644 --- a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs @@ -14,7 +14,7 @@ namespace System public partial class Array { [StructLayout(LayoutKind.Sequential)] - private sealed class RawData + internal sealed class RawData { public IntPtr Bounds; // The following is to prevent a mismatch between the managed and runtime @@ -67,7 +67,7 @@ public static unsafe void Clear(Array array) if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - ref byte ptr = ref array.GetRawSzArrayData(); + ref byte ptr = ref MemoryMarshal.GetArrayDataReference(array); nuint byteLength = array.NativeLength * (nuint)(uint)array.GetElementSize() /* force zero-extension */; if (RuntimeHelpers.ObjectHasReferences(array)) @@ -90,7 +90,7 @@ public static unsafe void Clear(Array array, int index, int length) if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > numComponents) ThrowHelper.ThrowIndexOutOfRangeException(); - ref byte ptr = ref Unsafe.AddByteOffset(ref array.GetRawSzArrayData(), (uint)offset * (nuint)elementSize); + ref byte ptr = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(array), (uint)offset * (nuint)elementSize); nuint byteLength = (uint)length * (nuint)elementSize; if (RuntimeHelpers.ObjectHasReferences(array)) @@ -450,22 +450,6 @@ public int GetUpperBound(int dimension) return GetLowerBound(dimension) + GetLength(dimension) - 1; } - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref byte GetRawSzArrayData() - { - // TODO: Missing intrinsic in interpreter - return ref Unsafe.As(this).Data; - } - - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref byte GetRawArrayData() - { - // TODO: Missing intrinsic in interpreter - return ref Unsafe.As(this).Data; - } - [Intrinsic] internal int GetElementSize() => GetElementSize(); diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.Mono.cs index 2b165f232648d6..7d7e14c3393f24 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.Mono.cs @@ -7,7 +7,7 @@ namespace System.Runtime.InteropServices { - public static partial class MemoryMarshal + public static unsafe partial class MemoryMarshal { /// /// Returns a reference to the 0th element of . If the array is empty, returns a reference to where the 0th element @@ -20,7 +20,26 @@ public static partial class MemoryMarshal /// [Intrinsic] [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetArrayDataReference(T[] array) => - ref Unsafe.As(ref array.GetRawArrayData()); + // Mono uses same layout for SZARRAY and MDARRAY; see comment on Array+RawData (in Array.Mono.cs) for details + ref Unsafe.As(ref Unsafe.As(array).Data); + + /// + /// Returns a reference to the 0th element of . If the array is empty, returns a reference to where the 0th element + /// would have been stored. Such a reference may be used for pinning but must never be dereferenced. + /// + /// is . + /// + /// The caller must manually reinterpret the returned ref byte as a ref to the array's underlying elemental type, + /// perhaps utilizing an API such as System.Runtime.CompilerServices.Unsafe.As to assist with the reinterpretation. + /// This technique does not perform array variance checks. The caller must manually perform any array variance checks + /// if the caller wishes to write to the returned reference. + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref byte GetArrayDataReference(Array array) => + // Mono uses same layout for SZARRAY and MDARRAY; see comment on Array+RawData (in Array.Mono.cs) for details + ref Unsafe.As(array).Data; } } diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index f09b05f67d33f4..f6a710f34a49b1 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2153,7 +2153,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas } } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.InteropServices") && !strcmp (klass_name, "MemoryMarshal")) { if (!strcmp (tm, "GetArrayDataReference")) - *op = MINT_INTRINS_MEMORYMARSHAL_GETARRAYDATAREF; + *op = MINT_INTRINS_MEMORYMARSHAL_GETARRAYDATAREF; // valid for both SZARRAY and MDARRAY } else if (in_corlib && !strcmp (klass_name_space, "System.Text.Unicode") && !strcmp (klass_name, "Utf16Utility")) { if (!strcmp (tm, "ConvertAllAsciiCharsInUInt32ToUppercase")) *op = MINT_INTRINS_ASCII_CHARS_TO_UPPERCASE; diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 519bcfb17cc869..d194525124dbd9 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -23,6 +23,7 @@ #include static GENERATE_GET_CLASS_WITH_CACHE (runtime_helpers, "System.Runtime.CompilerServices", "RuntimeHelpers") +static GENERATE_TRY_GET_CLASS_WITH_CACHE (memory_marshal, "System.Runtime.InteropServices", "MemoryMarshal") static GENERATE_TRY_GET_CLASS_WITH_CACHE (math, "System", "Math") /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic calls */ @@ -764,12 +765,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign return emit_array_generic_access (cfg, fsig, args, FALSE); else if (fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt && strcmp (cmethod->name, "SetGenericValueImpl") == 0) return emit_array_generic_access (cfg, fsig, args, TRUE); - else if (!strcmp (cmethod->name, "GetRawSzArrayData") || !strcmp (cmethod->name, "GetRawArrayData")) { - int dreg = alloc_preg (cfg); - MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg, FALSE); - EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, vector)); - return ins; - } else if (!strcmp (cmethod->name, "GetElementSize")) { int vt_reg = alloc_preg (cfg); int class_reg = alloc_preg (cfg); @@ -944,6 +939,14 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign return ins; } else return NULL; + } else if (cmethod->klass == mono_class_try_get_memory_marshal_class ()) { + if (!strcmp (cmethod->name, "GetArrayDataReference")) { + // Logic below works for both SZARRAY and MDARRAY + int dreg = alloc_preg (cfg); + MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg, FALSE); + EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, vector)); + return ins; + } } else if (cmethod->klass == mono_defaults.monitor_class) { gboolean is_enter = FALSE; gboolean is_v4 = FALSE;