Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
36 changes: 32 additions & 4 deletions src/coreclr/System.Private.CoreLib/src/System/ValueType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
**
===========================================================*/

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;

namespace System
{
Expand Down Expand Up @@ -42,7 +44,9 @@ public override bool Equals([NotNullWhen(true)] object? obj)
// if there are no GC references in this object we can avoid reflection
// and do a fast memcmp
if (CanCompareBits(this))
return FastEqualsCheck(thisObj, obj);
{
return SpanHelpers.SequenceEqual(ref RuntimeHelpers.GetRawData(this), ref RuntimeHelpers.GetRawData(obj), GetObjectSize(this));
}

FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

Expand Down Expand Up @@ -70,7 +74,7 @@ public override bool Equals([NotNullWhen(true)] object? obj)
private static extern bool CanCompareBits(object obj);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool FastEqualsCheck(object a, object b);
private static extern uint GetObjectSize(object obj);

/*=================================GetHashCode==================================
**Action: Our algorithm for returning the hashcode is a little bit complex. We look
Expand All @@ -85,8 +89,32 @@ public override bool Equals([NotNullWhen(true)] object? obj)
[MethodImpl(MethodImplOptions.InternalCall)]
public extern override int GetHashCode();

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int GetHashCodeOfPtr(IntPtr ptr);
private static int s_seed;

internal static int GetHashCodeOfPtr(IntPtr ptr)
{
int hashCode = (int)ptr;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is dropping the upper bits of a 64 bit pointer. Is this wise? All pointers offset by 4GB will result in the same hash code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is for runtime handles which are pointers to unmanaged tables. I don't expect they would span across 4GB.


if (hashCode == 0)
{
return 0;
}

int seed = s_seed;

// Initialize s_seed lazily
if (seed == 0)
{
// We use the first non-0 pointer as the seed, all hashcodes will be based off that.
// This is to make sure that we only reveal relative memory addresses and never absolute ones.
seed = hashCode;
Interlocked.CompareExchange(ref s_seed, seed, 0);
seed = s_seed;
}

Debug.Assert(s_seed != 0);
return hashCode - seed;
}

public override string? ToString()
{
Expand Down
52 changes: 7 additions & 45 deletions src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1827,6 +1827,13 @@ NOINLINE static FC_BOOL_RET CanCompareBitsHelper(MethodTable* mt, OBJECTREF objR
FC_RETURN_BOOL(ret);
}

FCIMPL1(UINT32, ValueTypeHelper::GetObjectSize, Object* obj)
{
FCALL_CONTRACT;
return obj->GetTypeHandle().GetSize();
}
FCIMPLEND

// Return true if the valuetype does not contain pointer, is tightly packed,
// does not have floating point number field and does not override Equals method.
FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
Expand All @@ -1847,21 +1854,6 @@ FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
}
FCIMPLEND

FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1, Object* obj2)
{
FCALL_CONTRACT;

_ASSERTE(obj1 != NULL);
_ASSERTE(obj2 != NULL);
_ASSERTE(!obj1->GetMethodTable()->ContainsPointers());
_ASSERTE(obj1->GetSize() == obj2->GetSize());

TypeHandle pTh = obj1->GetTypeHandle();

FC_RETURN_BOOL(memcmp(obj1->GetData(),obj2->GetData(),pTh.GetSize()) == 0);
}
FCIMPLEND

static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef)
{
CONTRACTL
Expand Down Expand Up @@ -2044,36 +2036,6 @@ FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE)
}
FCIMPLEND

static LONG s_dwSeed;

FCIMPL1(INT32, ValueTypeHelper::GetHashCodeOfPtr, LPVOID ptr)
{
FCALL_CONTRACT;

INT32 hashCode = (INT32)((INT64)(ptr));

if (hashCode == 0)
{
return 0;
}

DWORD dwSeed = s_dwSeed;

// Initialize s_dwSeed lazily
if (dwSeed == 0)
{
// We use the first non-0 pointer as the seed, all hashcodes will be based off that.
// This is to make sure that we only reveal relative memory addresses and never absolute ones.
dwSeed = hashCode;
InterlockedCompareExchange(&s_dwSeed, dwSeed, 0);
dwSeed = s_dwSeed;
}
_ASSERTE(dwSeed != 0);

return hashCode - dwSeed;
}
FCIMPLEND

static MethodTable * g_pStreamMT;
static WORD g_slotBeginRead, g_slotEndRead;
static WORD g_slotBeginWrite, g_slotEndWrite;
Expand Down
3 changes: 1 addition & 2 deletions src/coreclr/vm/comutilnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,8 @@ extern "C" void QCALLTYPE Interlocked_MemoryBarrierProcessWide();
class ValueTypeHelper {
public:
static FCDECL1(FC_BOOL_RET, CanCompareBits, Object* obj);
static FCDECL2(FC_BOOL_RET, FastEqualsCheck, Object* obj1, Object* obj2);
static FCDECL1(UINT32, GetObjectSize, Object* obj);
static FCDECL1(INT32, GetHashCode, Object* objRef);
static FCDECL1(INT32, GetHashCodeOfPtr, LPVOID ptr);
};

class StreamNative {
Expand Down
3 changes: 1 addition & 2 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,8 @@ FCFuncEnd()

FCFuncStart(gValueTypeFuncs)
FCFuncElement("CanCompareBits", ValueTypeHelper::CanCompareBits)
FCFuncElement("FastEqualsCheck", ValueTypeHelper::FastEqualsCheck)
FCFuncElement("GetObjectSize", ValueTypeHelper::GetObjectSize)
FCFuncElement("GetHashCode", ValueTypeHelper::GetHashCode)
FCFuncElement("GetHashCodeOfPtr", ValueTypeHelper::GetHashCodeOfPtr)
FCFuncEnd()

FCFuncStart(gDiagnosticsDebugger)
Expand Down