summaryrefslogtreecommitdiff
path: root/src/vm/stubcache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/stubcache.cpp')
-rw-r--r--src/vm/stubcache.cpp302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/vm/stubcache.cpp b/src/vm/stubcache.cpp
new file mode 100644
index 0000000000..70563eac8f
--- /dev/null
+++ b/src/vm/stubcache.cpp
@@ -0,0 +1,302 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: stubcache.cpp
+//
+
+//
+// Base class for caching stubs.
+//
+
+
+#include "common.h"
+#include "stubcache.h"
+#include "stublink.h"
+#include "cgensys.h"
+#include "excep.h"
+
+//---------------------------------------------------------
+// Constructor
+//---------------------------------------------------------
+StubCacheBase::StubCacheBase(LoaderHeap *pHeap) :
+ CClosedHashBase(
+#ifdef _DEBUG
+ 3,
+#else
+ 17, // CClosedHashTable will grow as necessary
+#endif
+
+ sizeof(STUBHASHENTRY),
+ FALSE
+ ),
+ m_crst(CrstStubCache),
+ m_heap(pHeap)
+{
+ WRAPPER_NO_CONTRACT;
+
+#ifdef FEATURE_PAL
+ if (m_heap == NULL)
+ m_heap = SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap();
+#endif
+
+}
+
+
+//---------------------------------------------------------
+// Destructor
+//---------------------------------------------------------
+StubCacheBase::~StubCacheBase()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ STUBHASHENTRY *phe = (STUBHASHENTRY*)GetFirst();
+ while (phe)
+ {
+ _ASSERTE(NULL != phe->m_pStub);
+ phe->m_pStub->DecRef();
+ phe = (STUBHASHENTRY*)GetNext((BYTE*)phe);
+ }
+}
+
+
+
+//---------------------------------------------------------
+// Returns the equivalent hashed Stub, creating a new hash
+// entry if necessary. If the latter, will call out to CompileStub.
+//
+// Refcounting:
+// The caller is responsible for DecRef'ing the returned stub in
+// order to avoid leaks.
+//---------------------------------------------------------
+Stub *StubCacheBase::Canonicalize(const BYTE * pRawStub)
+{
+ CONTRACT (Stub*)
+ {
+ STANDARD_VM_CHECK;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ STUBHASHENTRY *phe = NULL;
+
+ {
+ CrstHolder ch(&m_crst);
+
+ // Try to find the stub
+ phe = (STUBHASHENTRY*)Find((LPVOID)pRawStub);
+ if (phe)
+ {
+ StubHolder<Stub> pstub;
+ pstub = phe->m_pStub;
+
+ // IncRef as we're returning a reference to our caller.
+ pstub->IncRef();
+
+ pstub.SuppressRelease();
+ RETURN pstub;
+ }
+ }
+
+ // Couldn't find it, let's try to compile it.
+ CPUSTUBLINKER sl;
+ CPUSTUBLINKER *psl = &sl;
+ CompileStub(pRawStub, psl);
+
+ // Append the raw stub to the native stub
+ // and link up the stub.
+ CodeLabel *plabel = psl->EmitNewCodeLabel();
+ psl->EmitBytes(pRawStub, Length(pRawStub));
+ StubHolder<Stub> pstub;
+ pstub = psl->Link(m_heap);
+ UINT32 offset = psl->GetLabelOffset(plabel);
+
+ if (offset > 0xffff)
+ COMPlusThrowOM();
+
+ {
+ CrstHolder ch(&m_crst);
+
+ bool bNew;
+ phe = (STUBHASHENTRY*)FindOrAdd((LPVOID)pRawStub, /*modifies*/bNew);
+ if (phe)
+ {
+ if (bNew)
+ {
+ phe->m_pStub = pstub;
+ phe->m_offsetOfRawStub = (UINT16)offset;
+
+ AddStub(pRawStub, pstub);
+ }
+ else
+ {
+ // If we got here, some other thread got in
+ // and enregistered an identical stub during
+ // the window in which we were out of the m_crst.
+
+ //Under DEBUG, two identical ML streams can actually compile
+ // to different compiled stubs due to the checked build's
+ // toggling between inlined TLSGetValue and api TLSGetValue.
+ //_ASSERTE(phe->m_offsetOfRawStub == (UINT16)offset);
+
+ //Use the previously created stub
+ // This will DecRef the new stub for us.
+ pstub = phe->m_pStub;
+ }
+ // IncRef so that caller has firm ownership of stub.
+ pstub->IncRef();
+ }
+ }
+
+ if (!phe)
+ {
+ // Couldn't grow hash table due to lack of memory.
+ COMPlusThrowOM();
+ }
+
+ pstub.SuppressRelease();
+ RETURN pstub;
+}
+
+
+void StubCacheBase::AddStub(const BYTE* pRawStub, Stub* pNewStub)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // By default, don't do anything.
+ return;
+}
+
+
+//*****************************************************************************
+// Hash is called with a pointer to an element in the table. You must override
+// this method and provide a hash algorithm for your element type.
+//*****************************************************************************
+unsigned int StubCacheBase::Hash( // The key value.
+ void const *pData) // Raw data to hash.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ const BYTE *pRawStub = (const BYTE *)pData;
+
+ UINT cb = Length(pRawStub);
+ int hash = 0;
+ while (cb--)
+ hash = _rotl(hash,1) + *(pRawStub++);
+
+ return hash;
+}
+
+//*****************************************************************************
+// Compare is used in the typical memcmp way, 0 is eqaulity, -1/1 indicate
+// direction of miscompare. In this system everything is always equal or not.
+//*****************************************************************************
+unsigned int StubCacheBase::Compare( // 0, -1, or 1.
+ void const *pData, // Raw key data on lookup.
+ BYTE *pElement) // The element to compare data against.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ const BYTE *pRawStub1 = (const BYTE *)pData;
+ const BYTE *pRawStub2 = (const BYTE *)GetKey(pElement);
+ UINT cb1 = Length(pRawStub1);
+ UINT cb2 = Length(pRawStub2);
+
+ if (cb1 != cb2)
+ return 1; // not equal
+ else
+ {
+ while (cb1--)
+ {
+ if (*(pRawStub1++) != *(pRawStub2++))
+ return 1; // not equal
+ }
+ return 0;
+ }
+}
+
+//*****************************************************************************
+// Return true if the element is free to be used.
+//*****************************************************************************
+CClosedHashBase::ELEMENTSTATUS StubCacheBase::Status( // The status of the entry.
+ BYTE *pElement) // The element to check.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ Stub *pStub = ((STUBHASHENTRY*)pElement)->m_pStub;
+
+ if (pStub == NULL)
+ return FREE;
+ else if (pStub == (Stub*)(-1))
+ return DELETED;
+ else
+ return USED;
+}
+
+//*****************************************************************************
+// Sets the status of the given element.
+//*****************************************************************************
+void StubCacheBase::SetStatus(
+ BYTE *pElement, // The element to set status for.
+ ELEMENTSTATUS eStatus) // New status.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ STUBHASHENTRY *phe = (STUBHASHENTRY*)pElement;
+
+ switch (eStatus)
+ {
+ case FREE: phe->m_pStub = NULL; break;
+ case DELETED: phe->m_pStub = (Stub*)(-1); break;
+ default:
+ _ASSERTE(!"MLCacheEntry::SetStatus(): Bad argument.");
+ }
+}
+
+//*****************************************************************************
+// Returns the internal key value for an element.
+//*****************************************************************************
+void *StubCacheBase::GetKey( // The data to hash on.
+ BYTE *pElement) // The element to return data ptr for.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ STUBHASHENTRY *phe = (STUBHASHENTRY*)pElement;
+ return (void *)(phe->m_pStub->GetBlob() + phe->m_offsetOfRawStub);
+}