1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// File: stubcache.cpp
6//
7
8//
9// Base class for caching stubs.
10//
11
12
13#include "common.h"
14#include "stubcache.h"
15#include "stublink.h"
16#include "cgensys.h"
17#include "excep.h"
18
19//---------------------------------------------------------
20// Constructor
21//---------------------------------------------------------
22StubCacheBase::StubCacheBase(LoaderHeap *pHeap) :
23 CClosedHashBase(
24#ifdef _DEBUG
25 3,
26#else
27 17, // CClosedHashTable will grow as necessary
28#endif
29
30 sizeof(STUBHASHENTRY),
31 FALSE
32 ),
33 m_crst(CrstStubCache),
34 m_heap(pHeap)
35{
36 WRAPPER_NO_CONTRACT;
37
38#ifdef FEATURE_PAL
39 if (m_heap == NULL)
40 m_heap = SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap();
41#endif
42
43}
44
45
46//---------------------------------------------------------
47// Destructor
48//---------------------------------------------------------
49StubCacheBase::~StubCacheBase()
50{
51 CONTRACTL
52 {
53 NOTHROW;
54 GC_TRIGGERS;
55 MODE_ANY;
56 }
57 CONTRACTL_END;
58
59 STUBHASHENTRY *phe = (STUBHASHENTRY*)GetFirst();
60 while (phe)
61 {
62 _ASSERTE(NULL != phe->m_pStub);
63 phe->m_pStub->DecRef();
64 phe = (STUBHASHENTRY*)GetNext((BYTE*)phe);
65 }
66}
67
68
69
70//---------------------------------------------------------
71// Returns the equivalent hashed Stub, creating a new hash
72// entry if necessary. If the latter, will call out to CompileStub.
73//
74// Refcounting:
75// The caller is responsible for DecRef'ing the returned stub in
76// order to avoid leaks.
77//---------------------------------------------------------
78Stub *StubCacheBase::Canonicalize(const BYTE * pRawStub)
79{
80 CONTRACT (Stub*)
81 {
82 STANDARD_VM_CHECK;
83 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
84 }
85 CONTRACT_END;
86
87 STUBHASHENTRY *phe = NULL;
88
89 {
90 CrstHolder ch(&m_crst);
91
92 // Try to find the stub
93 phe = (STUBHASHENTRY*)Find((LPVOID)pRawStub);
94 if (phe)
95 {
96 StubHolder<Stub> pstub;
97 pstub = phe->m_pStub;
98
99 // IncRef as we're returning a reference to our caller.
100 pstub->IncRef();
101
102 pstub.SuppressRelease();
103 RETURN pstub;
104 }
105 }
106
107 // Couldn't find it, let's try to compile it.
108 CPUSTUBLINKER sl;
109 CPUSTUBLINKER *psl = &sl;
110 CompileStub(pRawStub, psl);
111
112 // Append the raw stub to the native stub
113 // and link up the stub.
114 CodeLabel *plabel = psl->EmitNewCodeLabel();
115 psl->EmitBytes(pRawStub, Length(pRawStub));
116 StubHolder<Stub> pstub;
117 pstub = psl->Link(m_heap);
118 UINT32 offset = psl->GetLabelOffset(plabel);
119
120 if (offset > 0xffff)
121 COMPlusThrowOM();
122
123 {
124 CrstHolder ch(&m_crst);
125
126 bool bNew;
127 phe = (STUBHASHENTRY*)FindOrAdd((LPVOID)pRawStub, /*modifies*/bNew);
128 if (phe)
129 {
130 if (bNew)
131 {
132 phe->m_pStub = pstub;
133 phe->m_offsetOfRawStub = (UINT16)offset;
134
135 AddStub(pRawStub, pstub);
136 }
137 else
138 {
139 // If we got here, some other thread got in
140 // and enregistered an identical stub during
141 // the window in which we were out of the m_crst.
142
143 //Under DEBUG, two identical ML streams can actually compile
144 // to different compiled stubs due to the checked build's
145 // toggling between inlined TLSGetValue and api TLSGetValue.
146 //_ASSERTE(phe->m_offsetOfRawStub == (UINT16)offset);
147
148 //Use the previously created stub
149 // This will DecRef the new stub for us.
150 pstub = phe->m_pStub;
151 }
152 // IncRef so that caller has firm ownership of stub.
153 pstub->IncRef();
154 }
155 }
156
157 if (!phe)
158 {
159 // Couldn't grow hash table due to lack of memory.
160 COMPlusThrowOM();
161 }
162
163 pstub.SuppressRelease();
164 RETURN pstub;
165}
166
167
168void StubCacheBase::AddStub(const BYTE* pRawStub, Stub* pNewStub)
169{
170 LIMITED_METHOD_CONTRACT;
171
172 // By default, don't do anything.
173 return;
174}
175
176
177//*****************************************************************************
178// Hash is called with a pointer to an element in the table. You must override
179// this method and provide a hash algorithm for your element type.
180//*****************************************************************************
181unsigned int StubCacheBase::Hash( // The key value.
182 void const *pData) // Raw data to hash.
183{
184 CONTRACTL
185 {
186 NOTHROW;
187 GC_NOTRIGGER;
188 MODE_ANY;
189 }
190 CONTRACTL_END;
191
192 const BYTE *pRawStub = (const BYTE *)pData;
193
194 UINT cb = Length(pRawStub);
195 int hash = 0;
196 while (cb--)
197 hash = _rotl(hash,1) + *(pRawStub++);
198
199 return hash;
200}
201
202//*****************************************************************************
203// Compare is used in the typical memcmp way, 0 is eqaulity, -1/1 indicate
204// direction of miscompare. In this system everything is always equal or not.
205//*****************************************************************************
206unsigned int StubCacheBase::Compare( // 0, -1, or 1.
207 void const *pData, // Raw key data on lookup.
208 BYTE *pElement) // The element to compare data against.
209{
210 CONTRACTL
211 {
212 NOTHROW;
213 GC_NOTRIGGER;
214 MODE_ANY;
215 }
216 CONTRACTL_END;
217
218 const BYTE *pRawStub1 = (const BYTE *)pData;
219 const BYTE *pRawStub2 = (const BYTE *)GetKey(pElement);
220 UINT cb1 = Length(pRawStub1);
221 UINT cb2 = Length(pRawStub2);
222
223 if (cb1 != cb2)
224 return 1; // not equal
225 else
226 {
227 while (cb1--)
228 {
229 if (*(pRawStub1++) != *(pRawStub2++))
230 return 1; // not equal
231 }
232 return 0;
233 }
234}
235
236//*****************************************************************************
237// Return true if the element is free to be used.
238//*****************************************************************************
239CClosedHashBase::ELEMENTSTATUS StubCacheBase::Status( // The status of the entry.
240 BYTE *pElement) // The element to check.
241{
242 CONTRACTL
243 {
244 NOTHROW;
245 GC_NOTRIGGER;
246 MODE_ANY;
247 }
248 CONTRACTL_END;
249
250 Stub *pStub = ((STUBHASHENTRY*)pElement)->m_pStub;
251
252 if (pStub == NULL)
253 return FREE;
254 else if (pStub == (Stub*)(-1))
255 return DELETED;
256 else
257 return USED;
258}
259
260//*****************************************************************************
261// Sets the status of the given element.
262//*****************************************************************************
263void StubCacheBase::SetStatus(
264 BYTE *pElement, // The element to set status for.
265 ELEMENTSTATUS eStatus) // New status.
266{
267 CONTRACTL
268 {
269 NOTHROW;
270 GC_NOTRIGGER;
271 MODE_ANY;
272 }
273 CONTRACTL_END;
274
275 STUBHASHENTRY *phe = (STUBHASHENTRY*)pElement;
276
277 switch (eStatus)
278 {
279 case FREE: phe->m_pStub = NULL; break;
280 case DELETED: phe->m_pStub = (Stub*)(-1); break;
281 default:
282 _ASSERTE(!"MLCacheEntry::SetStatus(): Bad argument.");
283 }
284}
285
286//*****************************************************************************
287// Returns the internal key value for an element.
288//*****************************************************************************
289void *StubCacheBase::GetKey( // The data to hash on.
290 BYTE *pElement) // The element to return data ptr for.
291{
292 CONTRACTL
293 {
294 NOTHROW;
295 GC_NOTRIGGER;
296 MODE_ANY;
297 }
298 CONTRACTL_END;
299
300 STUBHASHENTRY *phe = (STUBHASHENTRY*)pElement;
301 return (void *)(phe->m_pStub->GetBlob() + phe->m_offsetOfRawStub);
302}
303