Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/codert_vm/jithash.cpp
5985 views
1
/*******************************************************************************
2
* Copyright (c) 1991, 2021 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
#include <string.h>
24
#include "j9.h"
25
#include "j9protos.h"
26
#include "j9consts.h"
27
#include "jithash.h"
28
#include "AtomicSupport.hpp"
29
30
extern "C" {
31
32
#define BIT_MASK(bit) ((UDATA) 1 << (UDATA) (bit))
33
#define DETERMINE_BIT_SET(value, bit) ((UDATA) (value) & BIT_MASK(bit))
34
#define SET_BIT(value, bit) ((UDATA) (value) | BIT_MASK(bit))
35
#define REMOVE_BIT(value, bit) ((UDATA) (value) & ~BIT_MASK(bit))
36
37
#define LOW_BIT_SET(value) ((UDATA) (value) & (UDATA) 1)
38
#define SET_LOW_BIT(value) ((UDATA) (value) | (UDATA) 1)
39
#define REMOVE_LOW_BIT(value) ((UDATA) (value) & (UDATA) -2)
40
41
#define DETERMINE_BUCKET(value, start, buckets) (((((UDATA)(value) - (UDATA)(start)) >> (UDATA) 9) * (UDATA)sizeof(UDATA)) + (UDATA)(buckets))
42
#define BUCKET_SIZE 512
43
#define METHOD_STORE_SIZE 256
44
45
#define COUNT_MARK_BIT 20
46
47
48
J9JITExceptionTable**
49
hash_jit_artifact_array_insert(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable** array, J9JITExceptionTable *dataToInsert, UDATA startPC)
50
{
51
J9JITExceptionTable** returnVal = array;
52
PORT_ACCESS_FROM_PORT(portLibrary);
53
54
/* If the array pointer is tagged, then it's not a chain, it's a single tagged metadata */
55
56
if (LOW_BIT_SET(array)) {
57
/* There is a single tagged entry in the bucket, not a chain. In this case, we will
58
* always be allocating a new chain from the current allocate of the method store.
59
* We'll need 2 entries (one for the new entry and one for the existing tagged entry
60
* which will also terminate the chain.
61
*/
62
63
if ((table->currentAllocate + 2) > table->methodStoreEnd) { /* This comparison is safe since currentAllocate and methodStoreEnd will always be pointing into the same allocated block */
64
if (hash_jit_allocate_method_store(portLibrary, table) == NULL) {
65
return NULL;
66
}
67
}
68
returnVal = (J9JITExceptionTable**) table->currentAllocate;
69
table->currentAllocate += 2;
70
returnVal[0] = (J9JITExceptionTable *)dataToInsert;
71
returnVal[1] = (J9JITExceptionTable *)array;
72
} else {
73
J9JITExceptionTable** newElement;
74
75
/* There is already a chain, so we need to either add to the end of it if there is
76
* free space, or copy the chain and add to the end of the copy.
77
*/
78
79
newElement = array;
80
do {
81
++newElement;
82
} while (!LOW_BIT_SET(*(newElement-1)));
83
84
/* If there is a NULL at the newElement position, then we can just add there.
85
* It is safe to check *newElement even when the method store is full because
86
* the method store always has an extra non-NULL entry on the end.
87
*/
88
89
if (*newElement == NULL) {
90
/* Adding to the end of an existing chain with a free slot after it. To avoid twizzling
91
* bits, copy the existing chain end forward one slot, and place the new entry in the
92
* old end slot. A write barrier must be issued before storing the new element, both
93
* to ensure the metadata is fully visible before it can be seen in the array, and to
94
* make the new chain end visible.
95
*/
96
97
*newElement = *(newElement - 1);
98
VM_AtomicSupport::writeBarrier();
99
*(newElement - 1) = dataToInsert;
100
101
/* CMVC 117169 - increase the next allocation point only if the new element is not being stored into freed space */
102
if (newElement == (J9JITExceptionTable **) table->currentAllocate) {
103
table->currentAllocate += 1;
104
}
105
} else {
106
UDATA chainLength = newElement - array;
107
108
/* Not enough space to add to the end of the existing chain, so it must be copied
109
* and extended in free space. There may be enough free space in the current
110
* method store. Once space is found, copy the chain into it with the new entry at
111
* the beginning (to avoid twizzling tag bits). There's no need for a write barrier
112
* here since the new chain is not visible to anyone yet, and the caller of this
113
* function issues a write barrier before updating the bucket pointer.
114
*/
115
116
if ((table->currentAllocate + chainLength + 1) > table->methodStoreEnd) { /* This comparison is safe since currentAllocate and methodStoreEnd will always be pointing into the same allocated block */
117
if (hash_jit_allocate_method_store(portLibrary, table) == NULL) {
118
return NULL;
119
}
120
}
121
122
returnVal = (J9JITExceptionTable**) table->currentAllocate;
123
table->currentAllocate += (chainLength + 1);
124
returnVal[0] = dataToInsert;
125
memcpy(returnVal + 1, array, chainLength * sizeof(UDATA)); /* safe to memcpy since the new array is not yet visible */
126
}
127
}
128
129
return returnVal;
130
}
131
132
J9JITExceptionTable** hash_jit_artifact_array_remove(J9PortLibrary *portLibrary, J9JITExceptionTable** array, J9JITExceptionTable *dataToRemove) {
133
J9JITExceptionTable** index;
134
UDATA count= 0;
135
UDATA size;
136
UDATA removeSpot = 0;
137
PORT_ACCESS_FROM_PORT(portLibrary);
138
139
index = array;
140
141
while(!LOW_BIT_SET(*index)) { /* search for dataToRemove in the array */
142
++count;
143
if (*index == dataToRemove)
144
removeSpot = count;
145
++index;
146
}
147
148
if ((J9JITExceptionTable*) REMOVE_LOW_BIT(*index) == dataToRemove) {
149
*index=0; /* dataToRemove is last pointer in the array. */
150
--index;
151
*index = (J9JITExceptionTable*)SET_LOW_BIT(*index);
152
} else if(removeSpot) {
153
size = (count-removeSpot+1)*sizeof(UDATA); /* dataToRemove is in middle (or start) of the array. */
154
memmove((void*)(array+removeSpot-1),(void*)(array+removeSpot),size);
155
*index = 0;
156
} else {
157
return (J9JITExceptionTable**) 1; /* We did not find dataToRemove in array */
158
}
159
160
/* tidy the case of the array having contracted to becoming a single element - it can be recycled. */
161
if(LOW_BIT_SET(*array)) {
162
/* CMVC 117169 - NULL unused slots to allow re-use, and to simplify debugging */
163
J9JITExceptionTable ** rc = (J9JITExceptionTable**) *array; /* Only one pointer left. Just return the one pointer */
164
*array = NULL;
165
return rc;
166
}
167
168
return array;
169
}
170
171
UDATA
172
hash_jit_artifact_insert_range(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToInsert, UDATA startPC, UDATA endPC)
173
{
174
J9JITExceptionTable** index;
175
J9JITExceptionTable** endIndex;
176
J9JITExceptionTable** temp;
177
PORT_ACCESS_FROM_PORT(portLibrary);
178
179
if ((startPC < table->start) || (endPC > table->end)) {
180
return 1;
181
}
182
183
index = (J9JITExceptionTable **) DETERMINE_BUCKET(startPC, table->start, table->buckets);
184
endIndex = (J9JITExceptionTable **) DETERMINE_BUCKET(endPC, table->start, table->buckets);
185
186
do {
187
if (*index) {
188
temp = hash_jit_artifact_array_insert(portLibrary, table, (J9JITExceptionTable**) *index, dataToInsert, startPC);
189
if (!temp) {
190
return 2;
191
}
192
VM_AtomicSupport::writeBarrier();
193
*index = (J9JITExceptionTable *) temp;
194
} else {
195
VM_AtomicSupport::writeBarrier();
196
*index = (J9JITExceptionTable *) SET_LOW_BIT(dataToInsert);
197
}
198
199
} while (++index <= endIndex);
200
201
return 0;
202
}
203
204
UDATA hash_jit_artifact_insert(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToInsert) {
205
UDATA result;
206
207
result = hash_jit_artifact_insert_range(portLibrary, table, dataToInsert, dataToInsert->startPC, dataToInsert->endWarmPC);
208
if (result)
209
return result;
210
if (dataToInsert->startColdPC)
211
result = hash_jit_artifact_insert_range(portLibrary, table, dataToInsert, dataToInsert->startColdPC, dataToInsert->endPC);
212
213
return result;
214
}
215
216
UDATA hash_jit_artifact_remove_range(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToRemove, UDATA startPC, UDATA endPC) {
217
J9JITExceptionTable** index;
218
J9JITExceptionTable** endIndex;
219
J9JITExceptionTable* temp;
220
PORT_ACCESS_FROM_PORT(portLibrary);
221
222
if ((startPC < table->start) || (endPC > table->end))
223
return (UDATA) 1;
224
225
index = (J9JITExceptionTable**) DETERMINE_BUCKET(startPC, table->start, table->buckets);
226
endIndex = (J9JITExceptionTable**) DETERMINE_BUCKET(endPC, table->start, table->buckets);
227
do {
228
if (LOW_BIT_SET(*index)) {
229
if ((J9JITExceptionTable *)REMOVE_LOW_BIT(*index) == dataToRemove)
230
*index = 0;
231
else
232
return (UDATA) 1;
233
} else if (*index) {
234
temp = (J9JITExceptionTable*) hash_jit_artifact_array_remove(portLibrary, (J9JITExceptionTable**) *index, dataToRemove);
235
if (!temp) return (UDATA) 1;
236
else if (temp == (J9JITExceptionTable*) 1) return (UDATA) 2;
237
else
238
*index = temp;
239
} else
240
return (UDATA) 1;
241
} while (++index <= endIndex);
242
243
return (UDATA) 0;
244
}
245
246
UDATA hash_jit_artifact_remove(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToRemove) {
247
UDATA result;
248
249
result = hash_jit_artifact_remove_range(portLibrary, table, dataToRemove, dataToRemove->startPC, dataToRemove->endWarmPC);
250
if (result)
251
return result;
252
253
if (dataToRemove->startColdPC)
254
result = hash_jit_artifact_remove_range(portLibrary, table, dataToRemove, dataToRemove->startColdPC, dataToRemove->endPC);
255
return result;
256
}
257
258
J9JITHashTable *hash_jit_allocate(J9PortLibrary * portLibrary, UDATA start, UDATA end)
259
{
260
J9JITHashTable *table;
261
UDATA size;
262
PORT_ACCESS_FROM_PORT(portLibrary);
263
264
table = (J9JITHashTable *) j9mem_allocate_memory(sizeof(J9JITHashTable), OMRMEM_CATEGORY_JIT);
265
if (table == NULL) {
266
return NULL;
267
}
268
memset(table, 0, sizeof(*table));
269
table->start = start;
270
table->end = end;
271
272
size = DETERMINE_BUCKET(end, start, 0) + sizeof(UDATA);
273
table->buckets = (UDATA *) j9mem_allocate_memory(size, OMRMEM_CATEGORY_JIT);
274
if (table->buckets == NULL) {
275
j9mem_free_memory(table);
276
return NULL;
277
}
278
memset(table->buckets, 0, size);
279
280
if (hash_jit_allocate_method_store(portLibrary, table) == NULL) {
281
j9mem_free_memory(table->buckets);
282
j9mem_free_memory(table);
283
return NULL;
284
}
285
286
return table;
287
}
288
289
void hash_jit_free(J9PortLibrary * portLibrary, J9JITHashTable * table) {
290
UDATA *methodStoreCurrent, *methodStoreNext;
291
PORT_ACCESS_FROM_PORT(portLibrary);
292
293
methodStoreCurrent = table->methodStoreStart;
294
295
while (methodStoreCurrent) {
296
methodStoreNext = (UDATA *)*methodStoreCurrent;
297
j9mem_free_memory(methodStoreCurrent);
298
methodStoreCurrent = methodStoreNext;
299
}
300
j9mem_free_memory(table->buckets);
301
j9mem_free_memory(table);
302
}
303
304
J9JITHashTable* hash_jit_toJ9MemorySegment(J9JITHashTable * table, J9MemorySegment * codeCache, J9MemorySegment * dataCache) {
305
J9JITHashTable * dataCacheHashTable;
306
J9JITExceptionTable** index;
307
J9JITExceptionTable** endIndex;
308
J9JITExceptionTable** traversal;
309
J9JITExceptionTable** array;
310
UDATA start, end, bucketSize;
311
UDATA byteCount;
312
U_8* allocate;
313
U_8* arrayAllocate;
314
315
/* first determine amount of room needed for data structure */
316
index = (J9JITExceptionTable**) DETERMINE_BUCKET(table->start, table->start, table->buckets);
317
endIndex = (J9JITExceptionTable**) DETERMINE_BUCKET(table->end, table->start, table->buckets);
318
319
while ((*index == 0) && (index < endIndex)) ++index;
320
while ((*endIndex == 0) && (index <= endIndex)) --endIndex;
321
322
if (endIndex < index) return 0;
323
324
if (LOW_BIT_SET(*index)) {
325
start = ((J9JITExceptionTable *) REMOVE_LOW_BIT(*index))->startPC;
326
} else {
327
array = (J9JITExceptionTable**) *index;
328
/* assumes array contains more than one element */
329
start = (*array)->startPC;
330
++array;
331
332
while (!LOW_BIT_SET(*array)) {
333
if (start > (*array)->startPC)
334
start = (*array)->startPC;
335
++array;
336
}
337
338
array = (J9JITExceptionTable **) REMOVE_LOW_BIT(*array);
339
if (start > ((J9JITExceptionTable *) array)->startPC)
340
start = ((J9JITExceptionTable *) array)->startPC;
341
}
342
/* The start must be the same delta from the granularity (BUCKET_SIZE) */
343
/* as table->start was (which is the base of the code cache) */
344
start = ((start - table->start) / BUCKET_SIZE) * BUCKET_SIZE + table->start;
345
346
if (LOW_BIT_SET(*endIndex)) {
347
end = ((J9JITExceptionTable *) REMOVE_LOW_BIT(*endIndex))->endPC;
348
} else {
349
array = (J9JITExceptionTable**) *endIndex;
350
/* assumes array contains more than one element */
351
end = (*array)->endPC;
352
++array;
353
354
while (!LOW_BIT_SET(*array)) {
355
if (end < (*array)->endPC)
356
end = (*array)->endPC;
357
++array;
358
}
359
360
array = (J9JITExceptionTable **) REMOVE_LOW_BIT(*array);
361
if (end < ((J9JITExceptionTable *) array)->endPC)
362
end = ((J9JITExceptionTable *) array)->endPC;
363
}
364
365
bucketSize = DETERMINE_BUCKET(end, start, 0) + sizeof(UDATA);
366
byteCount = bucketSize;
367
368
traversal = index;
369
while (traversal <= endIndex) {
370
if (!LOW_BIT_SET(*traversal) && (*traversal)) {
371
byteCount += sizeof(UDATA);
372
array = (J9JITExceptionTable**) *traversal;
373
while (!LOW_BIT_SET(*array)) {
374
byteCount += sizeof(UDATA);
375
++array;
376
}
377
}
378
++traversal;
379
}
380
381
byteCount += sizeof(J9JITHashTable);
382
/* check alignment - currently removed */
383
/*if (LOW_BIT_SET(dataCache->heapAlloc))
384
++byteCount;*/
385
386
/* determine if enough room is available */
387
if ((UDATA)(dataCache->heapTop - dataCache->heapAlloc) < (byteCount + sizeof(J9JITDataCacheHeader)))
388
return 0;
389
390
allocate = dataCache->heapAlloc;
391
((J9JITDataCacheHeader *) allocate)->size = (U_32) (byteCount + sizeof(J9JITDataCacheHeader));
392
((J9JITDataCacheHeader *) allocate)->type = J9_JIT_DCE_HASH_TABLE;
393
394
allocate += sizeof(J9JITDataCacheHeader);
395
396
/* point buckets to first aligned chunk after the J9JITHashTable structure */
397
dataCacheHashTable = (J9JITHashTable *) allocate;
398
/* check alignment - currently removed */
399
/*if (LOW_BIT_SET(dataCache->heapAlloc))
400
dataCacheHashTable->buckets = (UDATA *) (allocate + sizeof(J9JITHashTable) + 1);
401
else*/
402
dataCacheHashTable->buckets = (UDATA *) (allocate + sizeof(J9JITHashTable));
403
404
dataCacheHashTable->parentAVLTreeNode.rightChild = dataCacheHashTable->parentAVLTreeNode.leftChild = 0;
405
dataCacheHashTable->flags = JIT_HASH_IN_DATA_CACHE;
406
407
dataCacheHashTable->start = start;
408
dataCacheHashTable->end = end;
409
allocate = (U_8 *) dataCacheHashTable->buckets;
410
arrayAllocate = allocate + bucketSize;
411
412
dataCache->heapAlloc += (byteCount + sizeof(J9JITDataCacheHeader));
413
414
/* traverse hash table inserting required data */
415
traversal = index;
416
while (traversal <= endIndex) {
417
if (LOW_BIT_SET(*traversal) || !(*traversal)) {
418
*((J9JITExceptionTable **) allocate) = *traversal;
419
} else {
420
*((J9JITExceptionTable ***) allocate) = (J9JITExceptionTable **) arrayAllocate;
421
422
array = (J9JITExceptionTable**) *traversal;
423
while (!LOW_BIT_SET(*array)) {
424
*((J9JITExceptionTable **) arrayAllocate) = *array;
425
arrayAllocate += sizeof(UDATA);
426
++array;
427
}
428
*((J9JITExceptionTable **) arrayAllocate) = *array;
429
arrayAllocate += sizeof(UDATA);
430
}
431
allocate += sizeof(UDATA);
432
++traversal;
433
}
434
435
return dataCacheHashTable;
436
}
437
438
/*
439
* Start walking the specified J9JITHashTable.
440
*
441
* Returns the first element in the table, or NULL if the table is empty
442
*
443
* See hash_jit_next_do
444
*/
445
J9JITExceptionTable * hash_jit_start_do(J9JITHashTableWalkState* walkState, J9JITHashTable* table) {
446
walkState->table = table;
447
walkState->index = 0;
448
walkState->bucket = NULL;
449
return hash_jit_next_do(walkState);
450
}
451
452
/*
453
* Iterate over a J9JITHashTable, using a walk state initialized by hash_jit_start_do
454
*
455
* Returns the next element in the table, or NULL if the table has no more elements
456
*
457
* Notes:
458
* 1) The same element may be returned more than once, since it may be stored in
459
* more than one hash bucket
460
* 2) It is not safe to call this function if the table has been modified (elements added
461
* or removed) since the last call the hash_jit_next_do or hash_jit_start_do
462
*/
463
J9JITExceptionTable * hash_jit_next_do(J9JITHashTableWalkState* walkState) {
464
J9JITHashTable* table = walkState->table;
465
UDATA size = DETERMINE_BUCKET(table->end, table->start, 0) / sizeof(UDATA) + 1;
466
J9JITExceptionTable* metaData;
467
468
while (walkState->bucket == NULL) {
469
UDATA bucket;
470
471
if (walkState->index >= size) return NULL;
472
473
bucket = table->buckets[walkState->index];
474
if (bucket == 0) {
475
walkState->index += 1;
476
} else if (LOW_BIT_SET(bucket)) {
477
/* just pretend that this is a list with one element */
478
walkState->bucket = &table->buckets[walkState->index];
479
} else {
480
walkState->bucket = (UDATA*)bucket;
481
}
482
}
483
484
metaData = (J9JITExceptionTable*)REMOVE_LOW_BIT(*walkState->bucket);
485
486
if ( LOW_BIT_SET(*walkState->bucket) ) {
487
walkState->bucket = NULL;
488
walkState->index += 1;
489
} else {
490
walkState->bucket += 1;
491
}
492
493
return metaData;
494
}
495
496
J9JITExceptionTable** hash_jit_allocate_method_store(J9PortLibrary *portLibrary, J9JITHashTable *table)
497
{
498
/* CMVC 117169 - add 1 slot for link, 1 slot for terminator */
499
UDATA size = (METHOD_STORE_SIZE + 2) * sizeof(UDATA);
500
UDATA* newStore;
501
PORT_ACCESS_FROM_PORT(portLibrary);
502
503
newStore = (UDATA *) j9mem_allocate_memory(size, OMRMEM_CATEGORY_JIT);
504
505
if (newStore != NULL) {
506
memset(newStore, 0, size);
507
508
*newStore = (UDATA) table->methodStoreStart; /* Link to the old store */
509
510
table->methodStoreStart = (UDATA *) newStore;
511
table->methodStoreEnd = (UDATA *) (newStore + METHOD_STORE_SIZE + 1);
512
table->currentAllocate = (UDATA *) (newStore + 1);
513
514
/* CMVC 117169 - Ensure that the method store is terminated with a non-NULL entry */
515
*(table->methodStoreEnd) = (UDATA) 0xBAAD076D;
516
}
517
518
return (J9JITExceptionTable**) newStore;
519
}
520
521
}
522
523