Skip to content

Commit

Permalink
Intrinsify Array GetArrayDataReference for SZ arrays (#87374)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichalPetryka authored Jul 17, 2023
1 parent 3c07a77 commit e89cfee
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static ref T GetArrayDataReference<T>(T[] array) =>
/// 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)
{
Expand Down
33 changes: 26 additions & 7 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2762,14 +2762,33 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
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, &notNull);
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,
Expand All @@ -2780,7 +2799,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
}

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public static ref T GetArrayDataReference<T>(T[] array) =>
/// 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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4737,7 +4737,7 @@ CorInfoType CEEInfo::getChildType (
}

/*********************************************************************/
// Check if this is a single dimensional array type
// Check if this is a single dimensional, zero based array type
bool CEEInfo::isSDArray(CORINFO_CLASS_HANDLE cls)
{
CONTRACTL {
Expand Down
92 changes: 92 additions & 0 deletions src/tests/JIT/Intrinsics/MemoryMarshalGetArrayDataReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,98 @@ unsafe static int Main(string[] args)
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)
Expand Down

0 comments on commit e89cfee

Please sign in to comment.