Add MemoryMarshal.GetArrayDataReference(Array) (#53562)
authorLevi Broderick <GrabYourPitchforks@users.noreply.github.com>
Thu, 3 Jun 2021 07:46:51 +0000 (00:46 -0700)
committerGitHub <noreply@github.com>
Thu, 3 Jun 2021 07:46:51 +0000 (00:46 -0700)
18 files changed:
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs
src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs
src/coreclr/vm/corelib.h
src/coreclr/vm/ilmarshalers.cpp
src/coreclr/vm/jitinterface.cpp
src/coreclr/vm/metasig.h
src/libraries/System.Memory/ref/System.Memory.cs
src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs
src/libraries/System.Private.CoreLib/src/System/Array.cs
src/libraries/System.Private.CoreLib/src/System/Buffer.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
src/mono/System.Private.CoreLib/src/System/Array.Mono.cs
src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.Mono.cs
src/mono/mono/mini/interp/transform.c
src/mono/mono/mini/intrinsics.c

index 3c4f41c81e17254313ca4106b6838c52c060c6a0..3137b834d75bd06a63fccde0ebfab84d6b6c75f8 100644 (file)
@@ -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 @@ namespace System
 
                 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 @@ namespace System
 
             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)
             {
index b4d95defcaa5bb1eb06f4627ab083a825ea440b9..6e3d8b57f504d3cb2f775c523f072070a1110601 100644 (file)
@@ -227,11 +227,6 @@ namespace System.Runtime.CompilerServices
             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<RawData>(array).Data, (nuint)GetMethodTable(array)->BaseSize - (nuint)(2 * sizeof(IntPtr)));
-
         internal static unsafe ushort GetElementSize(this Array array)
         {
             Debug.Assert(ObjectHasComponentSize(array));
index 021a0a2f52be1790a1cbeedb3bf49bd060e2a5f1..ba247054e4b46eee80df46786daef6462fc6ec36 100644 (file)
@@ -7,7 +7,7 @@ using Internal.Runtime.CompilerServices;
 
 namespace System.Runtime.InteropServices
 {
-    public static partial class MemoryMarshal
+    public static unsafe partial class MemoryMarshal
     {
         /// <summary>
         /// Returns a reference to the 0th element of <paramref name="array"/>. If the array is empty, returns a reference to where the 0th element
@@ -20,7 +20,29 @@ namespace System.Runtime.InteropServices
         /// </remarks>
         [Intrinsic]
         [NonVersionable]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static ref T GetArrayDataReference<T>(T[] array) =>
             ref Unsafe.As<byte, T>(ref Unsafe.As<RawArrayData>(array).Data);
+
+        /// <summary>
+        /// Returns a reference to the 0th element of <paramref name="array"/>. 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.
+        /// </summary>
+        /// <exception cref="NullReferenceException"><paramref name="array"/> is <see langword="null"/>.</exception>
+        /// <remarks>
+        /// The caller must manually reinterpret the returned <em>ref byte</em> as a ref to the array's underlying elemental type,
+        /// perhaps utilizing an API such as <em>System.Runtime.CompilerServices.Unsafe.As</em> 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.
+        /// </remarks>
+        [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<RawData>(array).Data, (nuint)RuntimeHelpers.GetMethodTable(array)->BaseSize - (nuint)(2 * sizeof(IntPtr)));
+        }
     }
 }
index fc1d74bf32636621c4d795ffa01a9ba5ee0397cf..fee30121c44f2164cc890177c001b168862ad032 100644 (file)
@@ -17,6 +17,11 @@ namespace Internal.IL.Stubs
             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>(T[])
+            }
+
             if (methodName == "GetArrayDataReference")
             {
                 ILEmitter emit = new ILEmitter();
index 5fe53b824e83a9dc69f261e327a1ac29d8e9d88a..73ce65fd2d76425e25bbd829845bb1708e9b4e09 100644 (file)
@@ -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)
index 3b45dc673c233240fe2819a890fa76b21f306be6..5c7517b28847c191f1a2931020565e2ba85340fb 100644 (file)
@@ -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);
index 02fae7f99f2bbae0bcb5e1c5594ea16bcf87ad74..a2df8bee06fc8ac6ab9d1d531498fc6c3a120176 100644 (file)
@@ -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();
 
index 3716ca9fd7b858ae2cdee41ec3bd5ea1c895470a..8c4b3f4a03e674003ae919ab0a2d665c52ab1e25 100644 (file)
@@ -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 ))
 
index e90b385ca656e2dbfacb6bf5f50f94547aae86c6..d9d629deb04e5aa6d4052f44eb5504398cc49c51 100644 (file)
@@ -505,6 +505,7 @@ namespace System.Runtime.InteropServices
         public static unsafe ReadOnlySpan<char> CreateReadOnlySpanFromNullTerminated(char* value) { throw null; }
         public static System.Span<T> CreateSpan<T>(ref T reference, int length) { throw null; }
         public static ref T GetArrayDataReference<T>(T[] array) { throw null; }
+        public static ref byte GetArrayDataReference(System.Array array) { throw null; }
         public static ref T GetReference<T>(System.ReadOnlySpan<T> span) { throw null; }
         public static ref T GetReference<T>(System.Span<T> span) { throw null; }
         public static T Read<T>(System.ReadOnlySpan<byte> source) where T : struct { throw null; }
index 5652d275f90cc7330cee0dbb8c63f37af3bf5d0d..c5ed1414d6a99fdb98f80629950e6f66440d5731 100644 (file)
@@ -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 @@ namespace System.SpanTests
         [ActiveIssue("https://github.com/dotnet/runtime/issues/36885", TestPlatforms.tvOS)]
         public static void GetArrayDataReference_NullInput_ThrowsNullRef()
         {
-            Assert.Throws<NullReferenceException>(() => MemoryMarshal.GetArrayDataReference((object[])null));
+            Assert.Throws<NullReferenceException>(() => MemoryMarshal.GetArrayDataReference<object>((object[])null));
+            Assert.Throws<NullReferenceException>(() => 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<byte, int>(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<byte, int>(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<byte, int>(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 @@ namespace System.SpanTests
 
             Assert.True(Unsafe.AsPointer(ref theRef) != null);
             Assert.True(Unsafe.AreSame(ref theRef, ref MemoryMarshal.GetReference(theArray.AsSpan())));
+
+            ref int theMdArrayRef = ref Unsafe.As<byte, int>(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<RawObject>(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<RawObject>(emptyArray).Data, ref MemoryMarshal.GetArrayDataReference(emptyArray));
+
+            Assert.Equal(baselineOffset, emptyArrayOffset);
         }
 
         [Fact]
@@ -45,5 +88,10 @@ namespace System.SpanTests
 
             Assert.True(Unsafe.AreSame(ref refObj, ref Unsafe.As<string, object>(ref strArr[0])));
         }
+
+        private sealed class RawObject
+        {
+            internal byte Data;
+        }
     }
 }
index 4879ff45deca8fe42053e3eb9e1cdc538800d6da..595283b1a58c309c830f690ca4a250308eecfb48 100644 (file)
@@ -2374,7 +2374,7 @@ namespace System
         }
 
         private static Span<T> UnsafeArrayAsSpan<T>(Array array, int adjustedIndex, int length) =>
-            new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawArrayData()), array.Length).Slice(adjustedIndex, length);
+            new Span<T>(ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)), array.Length).Slice(adjustedIndex, length);
 
         public IEnumerator GetEnumerator()
         {
index 5d9f18e91c574a7124fc5d3dbe78e76299fd885b..64bd6fca168e33efe6cfa3be05d390f98670823d 100644 (file)
@@ -60,7 +60,7 @@ namespace System
             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 @@ namespace System
                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
             }
 
-            return Unsafe.Add<byte>(ref array.GetRawArrayData(), index);
+            return Unsafe.Add<byte>(ref MemoryMarshal.GetArrayDataReference(array), index);
         }
 
         public static void SetByte(Array array, int index, byte value)
@@ -105,7 +105,7 @@ namespace System
                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
             }
 
-            Unsafe.Add<byte>(ref array.GetRawArrayData(), index) = value;
+            Unsafe.Add<byte>(ref MemoryMarshal.GetArrayDataReference(array), index) = value;
         }
 
         internal static unsafe void ZeroMemory(byte* dest, nuint len)
index b348c0b48161b71b81ebcd7d93e61f2501546806..78aa52682e417a823a66b370fd0990a1527e581c 100644 (file)
@@ -134,7 +134,7 @@ namespace System.Runtime.InteropServices
                     }
 
                     Debug.Assert(target is Array);
-                    return (IntPtr)Unsafe.AsPointer(ref Unsafe.As<Array>(target).GetRawArrayData());
+                    return (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As<Array>(target)));
                 }
 
                 return (IntPtr)Unsafe.AsPointer(ref target.GetRawData());
index 0f068648f7d7a2dfab0b60d951247c2a687383f3..15a9d86a224a96f9863355a914644332fffee962 100644 (file)
@@ -191,7 +191,7 @@ namespace System.Runtime.InteropServices
             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());
         }
 
index 7926abd1b721871a430359be5620a05fcc3f9750..94cb9eb7feea620bd596f76165a8a7aaf4a68c45 100644 (file)
@@ -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 @@ namespace System
             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 @@ namespace System
             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 @@ namespace System
             return GetLowerBound(dimension) + GetLength(dimension) - 1;
         }
 
-        [Intrinsic]
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal ref byte GetRawSzArrayData()
-        {
-            // TODO: Missing intrinsic in interpreter
-            return ref Unsafe.As<RawData>(this).Data;
-        }
-
-        [Intrinsic]
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal ref byte GetRawArrayData()
-        {
-            // TODO: Missing intrinsic in interpreter
-            return ref Unsafe.As<RawData>(this).Data;
-        }
-
         [Intrinsic]
         internal int GetElementSize() => GetElementSize();
 
index 2b165f232648d61bb53bd4049ff5bf0edc746bbc..7d7e14c3393f241826719fed5ce62d75283f61a0 100644 (file)
@@ -7,7 +7,7 @@ using Internal.Runtime.CompilerServices;
 
 namespace System.Runtime.InteropServices
 {
-    public static partial class MemoryMarshal
+    public static unsafe partial class MemoryMarshal
     {
         /// <summary>
         /// Returns a reference to the 0th element of <paramref name="array"/>. If the array is empty, returns a reference to where the 0th element
@@ -20,7 +20,26 @@ namespace System.Runtime.InteropServices
         /// </remarks>
         [Intrinsic]
         [NonVersionable]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static ref T GetArrayDataReference<T>(T[] array) =>
-            ref Unsafe.As<byte, T>(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<byte, T>(ref Unsafe.As<Array.RawData>(array).Data);
+
+        /// <summary>
+        /// Returns a reference to the 0th element of <paramref name="array"/>. 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.
+        /// </summary>
+        /// <exception cref="NullReferenceException"><paramref name="array"/> is <see langword="null"/>.</exception>
+        /// <remarks>
+        /// The caller must manually reinterpret the returned <em>ref byte</em> as a ref to the array's underlying elemental type,
+        /// perhaps utilizing an API such as <em>System.Runtime.CompilerServices.Unsafe.As</em> 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.
+        /// </remarks>
+        [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.RawData>(array).Data;
     }
 }
index f09b05f67d33f4169caabe940a0092c23a785155..f6a710f34a49b10eaa623eaea7b284bf53a7251b 100644 (file)
@@ -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;
index 519bcfb17cc869856d1a36146ff07bb32bfa7068..d194525124dbd9b6239aadb65ef896c9cc0f41f1 100644 (file)
@@ -23,6 +23,7 @@
 #include <mono/utils/mono-memory-model.h>
 
 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;