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 | //--------------------------------------------------------- |
22 | StubCacheBase::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 | //--------------------------------------------------------- |
49 | StubCacheBase::~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 | //--------------------------------------------------------- |
78 | Stub *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 | |
168 | void 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 | //***************************************************************************** |
181 | unsigned 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 | //***************************************************************************** |
206 | unsigned 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 | //***************************************************************************** |
239 | CClosedHashBase::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 | //***************************************************************************** |
263 | void 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 | //***************************************************************************** |
289 | void *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 | |