case NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference:
{
assert(sig->numArgs == 1);
- assert(sig->sigInst.methInstCount == 1);
- GenTree* array = impPopStack().val;
- CORINFO_CLASS_HANDLE elemHnd = sig->sigInst.methInst[0];
- CorInfoType jitType = info.compCompHnd->asCorInfoType(elemHnd);
- var_types elemType = JITtype2varType(jitType);
+ GenTree* array = impStackTop().val;
+ bool notNull = false;
+ CORINFO_CLASS_HANDLE elemHnd = NO_CLASS_HANDLE;
+ CorInfoType jitType;
+ if (sig->sigInst.methInstCount == 1)
+ {
+ elemHnd = sig->sigInst.methInst[0];
+ jitType = info.compCompHnd->asCorInfoType(elemHnd);
+ }
+ else
+ {
+ bool isExact = false;
+ CORINFO_CLASS_HANDLE arrayHnd = gtGetClassHandle(array, &isExact, ¬Null);
+ if ((arrayHnd == NO_CLASS_HANDLE) || !info.compCompHnd->isSDArray(arrayHnd))
+ {
+ return nullptr;
+ }
+ jitType = info.compCompHnd->getChildType(arrayHnd, &elemHnd);
+ }
+
+ array = impPopStack().val;
+
+ assert(jitType != CORINFO_TYPE_UNDEF);
+ assert((jitType != CORINFO_TYPE_VALUECLASS) || (elemHnd != NO_CLASS_HANDLE));
- if (fgAddrCouldBeNull(array))
+ if (!notNull && fgAddrCouldBeNull(array))
{
GenTree* arrayClone;
array = impCloneExpr(array, &arrayClone, CHECK_SPILL_ALL,
}
GenTree* index = gtNewIconNode(0, TYP_I_IMPL);
- GenTreeIndexAddr* indexAddr = gtNewArrayIndexAddr(array, index, elemType, elemHnd);
+ GenTreeIndexAddr* indexAddr = gtNewArrayIndexAddr(array, index, JITtype2varType(jitType), elemHnd);
indexAddr->gtFlags &= ~GTF_INX_RNGCHK;
indexAddr->gtFlags |= GTF_INX_ADDR_NONNULL;
retNode = indexAddr;
ThrowsNRE(() => ref ptrByte(NoInline<byte[]>(null)));
ThrowsNRE(() => ref ptrString(NoInline<string[]>(null)));
+ // use no inline methods to avoid indirect call inlining in the future
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static delegate*<Array, ref byte> GetMdPtr() => &MemoryMarshal.GetArrayDataReference;
+ delegate*<Array, ref byte> ptrMd = GetMdPtr();
+
+ IsTrue(Unsafe.AreSame(ref MemoryMarshal.GetArrayDataReference((Array)testByteArray), ref testByteArray[0]));
+ IsTrue(Unsafe.AreSame(ref ptrMd(testByteArray), ref testByteArray[0]));
+
+ IsTrue(Unsafe.AreSame(ref MemoryMarshal.GetArrayDataReference((Array)NoInline(testByteArray)), ref testByteArray[0]));
+ IsTrue(Unsafe.AreSame(ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(testByteArray)), ref testByteArray[0]));
+ IsTrue(Unsafe.AreSame(ref ptrMd(NoInline(testByteArray)), ref testByteArray[0]));
+
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref MemoryMarshal.GetArrayDataReference((Array)testStringArray)), ref testStringArray[0]));
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref ptrMd(testStringArray)), ref testStringArray[0]));
+
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref MemoryMarshal.GetArrayDataReference((Array)NoInline(testStringArray))), ref testStringArray[0]));
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(testStringArray))), ref testStringArray[0]));
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref ptrMd(NoInline(testStringArray))), ref testStringArray[0]));
+
+ byte[,] testByteMdArray = new byte[1, 1];
+ IsTrue(Unsafe.AreSame(ref MemoryMarshal.GetArrayDataReference(testByteMdArray), ref testByteMdArray[0, 0]));
+ IsTrue(Unsafe.AreSame(ref ptrMd(testByteMdArray), ref testByteMdArray[0, 0]));
+
+ IsTrue(Unsafe.AreSame(ref MemoryMarshal.GetArrayDataReference(NoInline(testByteMdArray)), ref testByteMdArray[0, 0]));
+ IsTrue(Unsafe.AreSame(ref ptrMd(NoInline(testByteMdArray)), ref testByteMdArray[0, 0]));
+
+ string[,] testStringMdArray = new string[1, 1];
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref MemoryMarshal.GetArrayDataReference(testStringMdArray)), ref testStringMdArray[0, 0]));
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref ptrMd(testStringMdArray)), ref testStringMdArray[0, 0]));
+
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref MemoryMarshal.GetArrayDataReference(NoInline(testStringMdArray))), ref testStringMdArray[0, 0]));
+ IsTrue(Unsafe.AreSame(ref Unsafe.As<byte, string>(ref ptrMd(NoInline(testStringMdArray))), ref testStringMdArray[0, 0]));
+
+ Array nonZeroArray = Array.CreateInstance(typeof(string), new [] { 1 }, new [] { -1 });
+ string test = "test";
+ nonZeroArray.SetValue(test, -1);
+ IsTrue(ReferenceEquals(Unsafe.As<byte, string>(ref MemoryMarshal.GetArrayDataReference(nonZeroArray)), test));
+ IsTrue(ReferenceEquals(Unsafe.As<byte, string>(ref ptrMd(nonZeroArray)), test));
+
+ IsTrue(ReferenceEquals(Unsafe.As<byte, string>(ref MemoryMarshal.GetArrayDataReference(NoInline(nonZeroArray))), test));
+ IsTrue(ReferenceEquals(Unsafe.As<byte, string>(ref ptrMd(NoInline(nonZeroArray))), test));
+
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference((Array)new byte[0])));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference((Array)new string[0])));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(new byte[0, 0])));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(new string[0, 0])));
+
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(new byte[0])));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(new string[0])));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(new byte[0, 0])));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(new string[0, 0])));
+
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference((Array)NoInline(new byte[0]))));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference((Array)NoInline(new string[0]))));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(NoInline(new byte[0, 0]))));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(NoInline(new string[0, 0]))));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(new byte[0]))));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(new string[0]))));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(new byte[0, 0]))));
+ IsFalse(Unsafe.IsNullRef(ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(new string[0, 0]))));
+
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline(new byte[0]))));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline(new string[0]))));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline(new byte[0, 0]))));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline(new string[0, 0]))));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline<Array>(new byte[0]))));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline<Array>(new string[0]))));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline<Array>(new byte[0, 0]))));
+ IsFalse(Unsafe.IsNullRef(ref ptrMd(NoInline<Array>(new string[0, 0]))));
+
+ ThrowsNRE(() => { _ = ref MemoryMarshal.GetArrayDataReference((Array)null); });
+ ThrowsNRE(() => { _ = ref ptrMd(null); });
+
+ ThrowsNRE(() => { _ = ref MemoryMarshal.GetArrayDataReference((Array)NoInline<byte[]>(null)); });
+ ThrowsNRE(() => { _ = ref MemoryMarshal.GetArrayDataReference((Array)NoInline<string[]>(null)); });
+ ThrowsNRE(() => { _ = ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(null)); });
+
+ ThrowsNRE(() => { _ = ref ptrMd(NoInline<byte[]>(null)); });
+ ThrowsNRE(() => { _ = ref ptrMd(NoInline<string[]>(null)); });
+ ThrowsNRE(() => { _ = ref ptrMd(NoInline<Array>(null)); });
+
+ ThrowsNRE(() => ref MemoryMarshal.GetArrayDataReference((Array)null));
+ ThrowsNRE(() => ref ptrMd(null));
+
+ ThrowsNRE(() => ref MemoryMarshal.GetArrayDataReference((Array)NoInline<byte[]>(null)));
+ ThrowsNRE(() => ref MemoryMarshal.GetArrayDataReference((Array)NoInline<string[]>(null)));
+ ThrowsNRE(() => ref MemoryMarshal.GetArrayDataReference(NoInline<Array>(null)));
+
+ ThrowsNRE(() => ref ptrMd(NoInline<byte[]>(null)));
+ ThrowsNRE(() => ref ptrMd(NoInline<string[]>(null)));
+ ThrowsNRE(() => ref ptrMd(NoInline<Array>(null)));
+
// from https://github.com/dotnet/runtime/issues/58312#issuecomment-993491291
[MethodImpl(MethodImplOptions.NoInlining)]
static int Problem1(StructWithByte[] a)