Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2976,10 +2976,18 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
}
break;

case GT_ARR_ADDR:
if ((op1->AsArrAddr()->GetElemType() != op2->AsArrAddr()->GetElemType()) ||
(op1->AsArrAddr()->GetElemClassHandle() != op2->AsArrAddr()->GetElemClassHandle()) ||
(op1->AsArrAddr()->GetFirstElemOffset() != op2->AsArrAddr()->GetFirstElemOffset()))
{
return false;
}
break;

// For the ones below no extra argument matters for comparison.
case GT_BOX:
case GT_RUNTIMELOOKUP:
case GT_ARR_ADDR:
break;

default:
Expand Down Expand Up @@ -3034,7 +3042,13 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
}
break;
case GT_INDEX_ADDR:
if (op1->AsIndexAddr()->gtElemSize != op2->AsIndexAddr()->gtElemSize)
if ((op1->AsIndexAddr()->gtElemSize != op2->AsIndexAddr()->gtElemSize) ||
(op1->AsIndexAddr()->gtElemType != op2->AsIndexAddr()->gtElemType) ||
(op1->AsIndexAddr()->gtStructElemClass != op2->AsIndexAddr()->gtStructElemClass) ||
(op1->AsIndexAddr()->gtLenOffset != op2->AsIndexAddr()->gtLenOffset) ||
(op1->AsIndexAddr()->gtElemOffset != op2->AsIndexAddr()->gtElemOffset) ||
((op1->gtFlags & (GTF_INX_RNGCHK | GTF_INX_ADDR_NONNULL)) !=
(op2->gtFlags & (GTF_INX_RNGCHK | GTF_INX_ADDR_NONNULL))))
{
return false;
}
Expand Down Expand Up @@ -3112,7 +3126,10 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
return false;
}

// NOTE: gtArrElemSize may need to be handled
Comment thread
EgorBo marked this conversation as resolved.
if (op1->AsArrElem()->gtArrElemSize != op2->AsArrElem()->gtArrElemSize)
{
return false;
}

unsigned dim;
for (dim = 0; dim < op1->AsArrElem()->gtArrRank; dim++)
Expand Down Expand Up @@ -3530,9 +3547,6 @@ unsigned Compiler::gtHashValue(GenTree* tree)
case GT_CAST:
hash ^= tree->AsCast()->gtCastType;
break;
case GT_INDEX_ADDR:
hash += tree->AsIndexAddr()->gtElemSize;
break;
case GT_ALLOCOBJ:
hash = genTreeHashAdd(hash, static_cast<unsigned>(
reinterpret_cast<uintptr_t>(tree->AsAllocObj()->gtAllocObjClsHnd)));
Expand All @@ -3554,7 +3568,13 @@ unsigned Compiler::gtHashValue(GenTree* tree)

// For the ones below no extra argument matters for comparison.
case GT_BOX:
break;

case GT_ARR_ADDR:
hash = genTreeHashAdd(hash, tree->AsArrAddr()->GetElemType());
hash = genTreeHashAdd(hash, static_cast<unsigned>(reinterpret_cast<uintptr_t>(
tree->AsArrAddr()->GetElemClassHandle())));
hash = genTreeHashAdd(hash, tree->AsArrAddr()->GetFirstElemOffset());
break;

default:
Expand Down Expand Up @@ -3600,7 +3620,17 @@ unsigned Compiler::gtHashValue(GenTree* tree)

// For the ones below no extra argument matters for comparison.
case GT_QMARK:
break;

case GT_INDEX_ADDR:
hash = genTreeHashAdd(hash, tree->AsIndexAddr()->gtElemSize);
hash = genTreeHashAdd(hash, tree->AsIndexAddr()->gtElemType);
hash = genTreeHashAdd(hash, static_cast<unsigned>(reinterpret_cast<uintptr_t>(
tree->AsIndexAddr()->gtStructElemClass)));
hash = genTreeHashAdd(hash, tree->AsIndexAddr()->gtLenOffset);
hash = genTreeHashAdd(hash, tree->AsIndexAddr()->gtElemOffset);
hash = genTreeHashAdd(hash, static_cast<unsigned>(tree->gtFlags &
(GTF_INX_RNGCHK | GTF_INX_ADDR_NONNULL)));
break;

#ifdef FEATURE_HW_INTRINSICS
Expand Down Expand Up @@ -3656,6 +3686,8 @@ unsigned Compiler::gtHashValue(GenTree* tree)
case GT_ARR_ELEM:

hash = genTreeHashAdd(hash, gtHashValue(tree->AsArrElem()->gtArrObj));
hash = genTreeHashAdd(hash, tree->AsArrElem()->gtArrRank);
hash = genTreeHashAdd(hash, tree->AsArrElem()->gtArrElemSize);

unsigned dim;
for (dim = 0; dim < tree->AsArrElem()->gtArrRank; dim++)
Expand Down
149 changes: 149 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_131459/Runtime_131459.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Runtime_131459;

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Xunit;

[StructLayout(LayoutKind.Sequential, Size = 16)]
internal struct S16
{
public byte Value;
}

[StructLayout(LayoutKind.Sequential, Size = 252)]
internal struct S252
{
public byte Value;
}

[StructLayout(LayoutKind.Sequential, Size = 256)]
internal struct S256
{
public byte Value;
}

[StructLayout(LayoutKind.Sequential, Size = 512)]
internal struct S512
{
public byte Value;
}

public class Runtime_131459
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static ref byte Address(bool small, object array)
{
if (small)
{
ref S16 element = ref Unsafe.As<S16[,]>(array)[0, 1];
return ref Unsafe.As<S16, byte>(ref element);
}
else
{
ref S252 element = ref Unsafe.As<S252[,]>(array)[0, 1];
return ref Unsafe.As<S252, byte>(ref element);
}
}

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static ref byte AddressBig(bool small, object array)
{
if (small)
{
ref S256 element = ref Unsafe.As<S256[,]>(array)[0, 1];
return ref Unsafe.As<S256, byte>(ref element);
}
else
{
ref S512 element = ref Unsafe.As<S512[,]>(array)[0, 1];
return ref Unsafe.As<S512, byte>(ref element);
}
}


[Fact]
public static void TestEntryPoint()
{
S16[,] small = new S16[1, 2];
small[0, 1].Value = 16;

S252[,] large = new S252[1, 2];
large[0, 1].Value = 252;

Assert.Equal(16, Address(true, small));
Assert.Equal(252, Address(false, large));

S256[,] smallBig = new S256[1, 2];
smallBig[0, 1].Value = 16;

S512[,] largeBig = new S512[1, 2];
largeBig[0, 1].Value = 252;

Assert.Equal(16, AddressBig(true, smallBig));
Assert.Equal(252, AddressBig(false, largeBig));
}

// GT_ARR_ADDR carries the array element type and class handle. If GenTree::Compare ignores
// them, tail merge folds the two arms below into one and the surviving node claims long[],
// which lets value numbering treat the store through 'r' as not aliasing da[1].
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static double ArrayElementTypeMerge(bool useLongArray, double[] da, object array)
{
ref byte r = ref Unsafe.NullRef<byte>();
if (useLongArray)
{
r = ref Unsafe.As<long, byte>(ref Unsafe.As<long[]>(array)[1]);
}
else
{
r = ref Unsafe.As<double, byte>(ref Unsafe.As<double[]>(array)[1]);
}

double before = da[1];
Unsafe.WriteUnaligned<long>(ref r, 0x4045000000000000L);
return da[1] - before;
}

[Fact]
public static void ArrayElementType()
{
double[] da = new double[2];
Assert.Equal(42.0, ArrayElementTypeMerge(false, da, da));
}

// GT_INDEX_ADDR also carries the "needs a range check" and "known non-null" bits.
// MemoryMarshal.GetArrayDataReference expands to an IndexAddr with the range check
// cleared, so if GenTree::Compare ignores those bits, tail merge can fold the two arms
// below and drop the range check on the a[0] path. The two trees only line up when the
// index constants have the same type, which is the case on 32-bit targets.
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static int BoundsCheckMerge(bool useDataReference, S16[] array)
{
S16 element;
if (useDataReference)
{
element = MemoryMarshal.GetArrayDataReference(array);
}
else
{
element = array[0];
}

return element.Value + 1;
}

[Fact]
public static void BoundsCheck()
{
S16[] nonEmpty = new S16[2];
Assert.Equal(1, BoundsCheckMerge(true, nonEmpty));
Assert.Equal(1, BoundsCheckMerge(false, nonEmpty));

S16[] empty = new S16[0];
Assert.Throws<IndexOutOfRangeException>(() => BoundsCheckMerge(false, empty));
}
}
1 change: 1 addition & 0 deletions src/tests/JIT/Regression/Regression_ro_2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
<Compile Include="JitBlue\Runtime_130830\Runtime_130830.cs" />
<Compile Include="JitBlue\Runtime_130831\Runtime_130831.cs" />
<Compile Include="JitBlue\Runtime_131137\Runtime_131137.cs" />
<Compile Include="JitBlue\Runtime_131459\Runtime_131459.cs" />
<Compile Include="JitBlue\Runtime_10337\Runtime_10337.cs" />
<Compile Include="JitBlue\Runtime_125160\Runtime_125160.cs" />
</ItemGroup>
Expand Down
Loading