Path: blob/master/runtime/codert_vm/jithash.cpp
5985 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/2122#include <string.h>23#include "j9.h"24#include "j9protos.h"25#include "j9consts.h"26#include "jithash.h"27#include "AtomicSupport.hpp"2829extern "C" {3031#define BIT_MASK(bit) ((UDATA) 1 << (UDATA) (bit))32#define DETERMINE_BIT_SET(value, bit) ((UDATA) (value) & BIT_MASK(bit))33#define SET_BIT(value, bit) ((UDATA) (value) | BIT_MASK(bit))34#define REMOVE_BIT(value, bit) ((UDATA) (value) & ~BIT_MASK(bit))3536#define LOW_BIT_SET(value) ((UDATA) (value) & (UDATA) 1)37#define SET_LOW_BIT(value) ((UDATA) (value) | (UDATA) 1)38#define REMOVE_LOW_BIT(value) ((UDATA) (value) & (UDATA) -2)3940#define DETERMINE_BUCKET(value, start, buckets) (((((UDATA)(value) - (UDATA)(start)) >> (UDATA) 9) * (UDATA)sizeof(UDATA)) + (UDATA)(buckets))41#define BUCKET_SIZE 51242#define METHOD_STORE_SIZE 2564344#define COUNT_MARK_BIT 20454647J9JITExceptionTable**48hash_jit_artifact_array_insert(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable** array, J9JITExceptionTable *dataToInsert, UDATA startPC)49{50J9JITExceptionTable** returnVal = array;51PORT_ACCESS_FROM_PORT(portLibrary);5253/* If the array pointer is tagged, then it's not a chain, it's a single tagged metadata */5455if (LOW_BIT_SET(array)) {56/* There is a single tagged entry in the bucket, not a chain. In this case, we will57* always be allocating a new chain from the current allocate of the method store.58* We'll need 2 entries (one for the new entry and one for the existing tagged entry59* which will also terminate the chain.60*/6162if ((table->currentAllocate + 2) > table->methodStoreEnd) { /* This comparison is safe since currentAllocate and methodStoreEnd will always be pointing into the same allocated block */63if (hash_jit_allocate_method_store(portLibrary, table) == NULL) {64return NULL;65}66}67returnVal = (J9JITExceptionTable**) table->currentAllocate;68table->currentAllocate += 2;69returnVal[0] = (J9JITExceptionTable *)dataToInsert;70returnVal[1] = (J9JITExceptionTable *)array;71} else {72J9JITExceptionTable** newElement;7374/* There is already a chain, so we need to either add to the end of it if there is75* free space, or copy the chain and add to the end of the copy.76*/7778newElement = array;79do {80++newElement;81} while (!LOW_BIT_SET(*(newElement-1)));8283/* If there is a NULL at the newElement position, then we can just add there.84* It is safe to check *newElement even when the method store is full because85* the method store always has an extra non-NULL entry on the end.86*/8788if (*newElement == NULL) {89/* Adding to the end of an existing chain with a free slot after it. To avoid twizzling90* bits, copy the existing chain end forward one slot, and place the new entry in the91* old end slot. A write barrier must be issued before storing the new element, both92* to ensure the metadata is fully visible before it can be seen in the array, and to93* make the new chain end visible.94*/9596*newElement = *(newElement - 1);97VM_AtomicSupport::writeBarrier();98*(newElement - 1) = dataToInsert;99100/* CMVC 117169 - increase the next allocation point only if the new element is not being stored into freed space */101if (newElement == (J9JITExceptionTable **) table->currentAllocate) {102table->currentAllocate += 1;103}104} else {105UDATA chainLength = newElement - array;106107/* Not enough space to add to the end of the existing chain, so it must be copied108* and extended in free space. There may be enough free space in the current109* method store. Once space is found, copy the chain into it with the new entry at110* the beginning (to avoid twizzling tag bits). There's no need for a write barrier111* here since the new chain is not visible to anyone yet, and the caller of this112* function issues a write barrier before updating the bucket pointer.113*/114115if ((table->currentAllocate + chainLength + 1) > table->methodStoreEnd) { /* This comparison is safe since currentAllocate and methodStoreEnd will always be pointing into the same allocated block */116if (hash_jit_allocate_method_store(portLibrary, table) == NULL) {117return NULL;118}119}120121returnVal = (J9JITExceptionTable**) table->currentAllocate;122table->currentAllocate += (chainLength + 1);123returnVal[0] = dataToInsert;124memcpy(returnVal + 1, array, chainLength * sizeof(UDATA)); /* safe to memcpy since the new array is not yet visible */125}126}127128return returnVal;129}130131J9JITExceptionTable** hash_jit_artifact_array_remove(J9PortLibrary *portLibrary, J9JITExceptionTable** array, J9JITExceptionTable *dataToRemove) {132J9JITExceptionTable** index;133UDATA count= 0;134UDATA size;135UDATA removeSpot = 0;136PORT_ACCESS_FROM_PORT(portLibrary);137138index = array;139140while(!LOW_BIT_SET(*index)) { /* search for dataToRemove in the array */141++count;142if (*index == dataToRemove)143removeSpot = count;144++index;145}146147if ((J9JITExceptionTable*) REMOVE_LOW_BIT(*index) == dataToRemove) {148*index=0; /* dataToRemove is last pointer in the array. */149--index;150*index = (J9JITExceptionTable*)SET_LOW_BIT(*index);151} else if(removeSpot) {152size = (count-removeSpot+1)*sizeof(UDATA); /* dataToRemove is in middle (or start) of the array. */153memmove((void*)(array+removeSpot-1),(void*)(array+removeSpot),size);154*index = 0;155} else {156return (J9JITExceptionTable**) 1; /* We did not find dataToRemove in array */157}158159/* tidy the case of the array having contracted to becoming a single element - it can be recycled. */160if(LOW_BIT_SET(*array)) {161/* CMVC 117169 - NULL unused slots to allow re-use, and to simplify debugging */162J9JITExceptionTable ** rc = (J9JITExceptionTable**) *array; /* Only one pointer left. Just return the one pointer */163*array = NULL;164return rc;165}166167return array;168}169170UDATA171hash_jit_artifact_insert_range(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToInsert, UDATA startPC, UDATA endPC)172{173J9JITExceptionTable** index;174J9JITExceptionTable** endIndex;175J9JITExceptionTable** temp;176PORT_ACCESS_FROM_PORT(portLibrary);177178if ((startPC < table->start) || (endPC > table->end)) {179return 1;180}181182index = (J9JITExceptionTable **) DETERMINE_BUCKET(startPC, table->start, table->buckets);183endIndex = (J9JITExceptionTable **) DETERMINE_BUCKET(endPC, table->start, table->buckets);184185do {186if (*index) {187temp = hash_jit_artifact_array_insert(portLibrary, table, (J9JITExceptionTable**) *index, dataToInsert, startPC);188if (!temp) {189return 2;190}191VM_AtomicSupport::writeBarrier();192*index = (J9JITExceptionTable *) temp;193} else {194VM_AtomicSupport::writeBarrier();195*index = (J9JITExceptionTable *) SET_LOW_BIT(dataToInsert);196}197198} while (++index <= endIndex);199200return 0;201}202203UDATA hash_jit_artifact_insert(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToInsert) {204UDATA result;205206result = hash_jit_artifact_insert_range(portLibrary, table, dataToInsert, dataToInsert->startPC, dataToInsert->endWarmPC);207if (result)208return result;209if (dataToInsert->startColdPC)210result = hash_jit_artifact_insert_range(portLibrary, table, dataToInsert, dataToInsert->startColdPC, dataToInsert->endPC);211212return result;213}214215UDATA hash_jit_artifact_remove_range(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToRemove, UDATA startPC, UDATA endPC) {216J9JITExceptionTable** index;217J9JITExceptionTable** endIndex;218J9JITExceptionTable* temp;219PORT_ACCESS_FROM_PORT(portLibrary);220221if ((startPC < table->start) || (endPC > table->end))222return (UDATA) 1;223224index = (J9JITExceptionTable**) DETERMINE_BUCKET(startPC, table->start, table->buckets);225endIndex = (J9JITExceptionTable**) DETERMINE_BUCKET(endPC, table->start, table->buckets);226do {227if (LOW_BIT_SET(*index)) {228if ((J9JITExceptionTable *)REMOVE_LOW_BIT(*index) == dataToRemove)229*index = 0;230else231return (UDATA) 1;232} else if (*index) {233temp = (J9JITExceptionTable*) hash_jit_artifact_array_remove(portLibrary, (J9JITExceptionTable**) *index, dataToRemove);234if (!temp) return (UDATA) 1;235else if (temp == (J9JITExceptionTable*) 1) return (UDATA) 2;236else237*index = temp;238} else239return (UDATA) 1;240} while (++index <= endIndex);241242return (UDATA) 0;243}244245UDATA hash_jit_artifact_remove(J9PortLibrary *portLibrary, J9JITHashTable *table, J9JITExceptionTable *dataToRemove) {246UDATA result;247248result = hash_jit_artifact_remove_range(portLibrary, table, dataToRemove, dataToRemove->startPC, dataToRemove->endWarmPC);249if (result)250return result;251252if (dataToRemove->startColdPC)253result = hash_jit_artifact_remove_range(portLibrary, table, dataToRemove, dataToRemove->startColdPC, dataToRemove->endPC);254return result;255}256257J9JITHashTable *hash_jit_allocate(J9PortLibrary * portLibrary, UDATA start, UDATA end)258{259J9JITHashTable *table;260UDATA size;261PORT_ACCESS_FROM_PORT(portLibrary);262263table = (J9JITHashTable *) j9mem_allocate_memory(sizeof(J9JITHashTable), OMRMEM_CATEGORY_JIT);264if (table == NULL) {265return NULL;266}267memset(table, 0, sizeof(*table));268table->start = start;269table->end = end;270271size = DETERMINE_BUCKET(end, start, 0) + sizeof(UDATA);272table->buckets = (UDATA *) j9mem_allocate_memory(size, OMRMEM_CATEGORY_JIT);273if (table->buckets == NULL) {274j9mem_free_memory(table);275return NULL;276}277memset(table->buckets, 0, size);278279if (hash_jit_allocate_method_store(portLibrary, table) == NULL) {280j9mem_free_memory(table->buckets);281j9mem_free_memory(table);282return NULL;283}284285return table;286}287288void hash_jit_free(J9PortLibrary * portLibrary, J9JITHashTable * table) {289UDATA *methodStoreCurrent, *methodStoreNext;290PORT_ACCESS_FROM_PORT(portLibrary);291292methodStoreCurrent = table->methodStoreStart;293294while (methodStoreCurrent) {295methodStoreNext = (UDATA *)*methodStoreCurrent;296j9mem_free_memory(methodStoreCurrent);297methodStoreCurrent = methodStoreNext;298}299j9mem_free_memory(table->buckets);300j9mem_free_memory(table);301}302303J9JITHashTable* hash_jit_toJ9MemorySegment(J9JITHashTable * table, J9MemorySegment * codeCache, J9MemorySegment * dataCache) {304J9JITHashTable * dataCacheHashTable;305J9JITExceptionTable** index;306J9JITExceptionTable** endIndex;307J9JITExceptionTable** traversal;308J9JITExceptionTable** array;309UDATA start, end, bucketSize;310UDATA byteCount;311U_8* allocate;312U_8* arrayAllocate;313314/* first determine amount of room needed for data structure */315index = (J9JITExceptionTable**) DETERMINE_BUCKET(table->start, table->start, table->buckets);316endIndex = (J9JITExceptionTable**) DETERMINE_BUCKET(table->end, table->start, table->buckets);317318while ((*index == 0) && (index < endIndex)) ++index;319while ((*endIndex == 0) && (index <= endIndex)) --endIndex;320321if (endIndex < index) return 0;322323if (LOW_BIT_SET(*index)) {324start = ((J9JITExceptionTable *) REMOVE_LOW_BIT(*index))->startPC;325} else {326array = (J9JITExceptionTable**) *index;327/* assumes array contains more than one element */328start = (*array)->startPC;329++array;330331while (!LOW_BIT_SET(*array)) {332if (start > (*array)->startPC)333start = (*array)->startPC;334++array;335}336337array = (J9JITExceptionTable **) REMOVE_LOW_BIT(*array);338if (start > ((J9JITExceptionTable *) array)->startPC)339start = ((J9JITExceptionTable *) array)->startPC;340}341/* The start must be the same delta from the granularity (BUCKET_SIZE) */342/* as table->start was (which is the base of the code cache) */343start = ((start - table->start) / BUCKET_SIZE) * BUCKET_SIZE + table->start;344345if (LOW_BIT_SET(*endIndex)) {346end = ((J9JITExceptionTable *) REMOVE_LOW_BIT(*endIndex))->endPC;347} else {348array = (J9JITExceptionTable**) *endIndex;349/* assumes array contains more than one element */350end = (*array)->endPC;351++array;352353while (!LOW_BIT_SET(*array)) {354if (end < (*array)->endPC)355end = (*array)->endPC;356++array;357}358359array = (J9JITExceptionTable **) REMOVE_LOW_BIT(*array);360if (end < ((J9JITExceptionTable *) array)->endPC)361end = ((J9JITExceptionTable *) array)->endPC;362}363364bucketSize = DETERMINE_BUCKET(end, start, 0) + sizeof(UDATA);365byteCount = bucketSize;366367traversal = index;368while (traversal <= endIndex) {369if (!LOW_BIT_SET(*traversal) && (*traversal)) {370byteCount += sizeof(UDATA);371array = (J9JITExceptionTable**) *traversal;372while (!LOW_BIT_SET(*array)) {373byteCount += sizeof(UDATA);374++array;375}376}377++traversal;378}379380byteCount += sizeof(J9JITHashTable);381/* check alignment - currently removed */382/*if (LOW_BIT_SET(dataCache->heapAlloc))383++byteCount;*/384385/* determine if enough room is available */386if ((UDATA)(dataCache->heapTop - dataCache->heapAlloc) < (byteCount + sizeof(J9JITDataCacheHeader)))387return 0;388389allocate = dataCache->heapAlloc;390((J9JITDataCacheHeader *) allocate)->size = (U_32) (byteCount + sizeof(J9JITDataCacheHeader));391((J9JITDataCacheHeader *) allocate)->type = J9_JIT_DCE_HASH_TABLE;392393allocate += sizeof(J9JITDataCacheHeader);394395/* point buckets to first aligned chunk after the J9JITHashTable structure */396dataCacheHashTable = (J9JITHashTable *) allocate;397/* check alignment - currently removed */398/*if (LOW_BIT_SET(dataCache->heapAlloc))399dataCacheHashTable->buckets = (UDATA *) (allocate + sizeof(J9JITHashTable) + 1);400else*/401dataCacheHashTable->buckets = (UDATA *) (allocate + sizeof(J9JITHashTable));402403dataCacheHashTable->parentAVLTreeNode.rightChild = dataCacheHashTable->parentAVLTreeNode.leftChild = 0;404dataCacheHashTable->flags = JIT_HASH_IN_DATA_CACHE;405406dataCacheHashTable->start = start;407dataCacheHashTable->end = end;408allocate = (U_8 *) dataCacheHashTable->buckets;409arrayAllocate = allocate + bucketSize;410411dataCache->heapAlloc += (byteCount + sizeof(J9JITDataCacheHeader));412413/* traverse hash table inserting required data */414traversal = index;415while (traversal <= endIndex) {416if (LOW_BIT_SET(*traversal) || !(*traversal)) {417*((J9JITExceptionTable **) allocate) = *traversal;418} else {419*((J9JITExceptionTable ***) allocate) = (J9JITExceptionTable **) arrayAllocate;420421array = (J9JITExceptionTable**) *traversal;422while (!LOW_BIT_SET(*array)) {423*((J9JITExceptionTable **) arrayAllocate) = *array;424arrayAllocate += sizeof(UDATA);425++array;426}427*((J9JITExceptionTable **) arrayAllocate) = *array;428arrayAllocate += sizeof(UDATA);429}430allocate += sizeof(UDATA);431++traversal;432}433434return dataCacheHashTable;435}436437/*438* Start walking the specified J9JITHashTable.439*440* Returns the first element in the table, or NULL if the table is empty441*442* See hash_jit_next_do443*/444J9JITExceptionTable * hash_jit_start_do(J9JITHashTableWalkState* walkState, J9JITHashTable* table) {445walkState->table = table;446walkState->index = 0;447walkState->bucket = NULL;448return hash_jit_next_do(walkState);449}450451/*452* Iterate over a J9JITHashTable, using a walk state initialized by hash_jit_start_do453*454* Returns the next element in the table, or NULL if the table has no more elements455*456* Notes:457* 1) The same element may be returned more than once, since it may be stored in458* more than one hash bucket459* 2) It is not safe to call this function if the table has been modified (elements added460* or removed) since the last call the hash_jit_next_do or hash_jit_start_do461*/462J9JITExceptionTable * hash_jit_next_do(J9JITHashTableWalkState* walkState) {463J9JITHashTable* table = walkState->table;464UDATA size = DETERMINE_BUCKET(table->end, table->start, 0) / sizeof(UDATA) + 1;465J9JITExceptionTable* metaData;466467while (walkState->bucket == NULL) {468UDATA bucket;469470if (walkState->index >= size) return NULL;471472bucket = table->buckets[walkState->index];473if (bucket == 0) {474walkState->index += 1;475} else if (LOW_BIT_SET(bucket)) {476/* just pretend that this is a list with one element */477walkState->bucket = &table->buckets[walkState->index];478} else {479walkState->bucket = (UDATA*)bucket;480}481}482483metaData = (J9JITExceptionTable*)REMOVE_LOW_BIT(*walkState->bucket);484485if ( LOW_BIT_SET(*walkState->bucket) ) {486walkState->bucket = NULL;487walkState->index += 1;488} else {489walkState->bucket += 1;490}491492return metaData;493}494495J9JITExceptionTable** hash_jit_allocate_method_store(J9PortLibrary *portLibrary, J9JITHashTable *table)496{497/* CMVC 117169 - add 1 slot for link, 1 slot for terminator */498UDATA size = (METHOD_STORE_SIZE + 2) * sizeof(UDATA);499UDATA* newStore;500PORT_ACCESS_FROM_PORT(portLibrary);501502newStore = (UDATA *) j9mem_allocate_memory(size, OMRMEM_CATEGORY_JIT);503504if (newStore != NULL) {505memset(newStore, 0, size);506507*newStore = (UDATA) table->methodStoreStart; /* Link to the old store */508509table->methodStoreStart = (UDATA *) newStore;510table->methodStoreEnd = (UDATA *) (newStore + METHOD_STORE_SIZE + 1);511table->currentAllocate = (UDATA *) (newStore + 1);512513/* CMVC 117169 - Ensure that the method store is terminated with a non-NULL entry */514*(table->methodStoreEnd) = (UDATA) 0xBAAD076D;515}516517return (J9JITExceptionTable**) newStore;518}519520}521522523