From 6cbd24a383f0155b8db3a16ad62bb46babd83ab8 Mon Sep 17 00:00:00 2001 From: Harry Yu Date: Wed, 18 Sep 2019 13:46:35 -0400 Subject: [PATCH] Port j9methodserver from jitaas to master - Introduce class `TR_ResolvedJ9JITServerMethod`. It is a class used by JITServer for obtaining method/class information needed during compilation from JITClient. This class is an extension of the `TR_ResolvedJ9Method` class. Most of the APIs of `TR_ResolvedJ9JITServerMethod` are remote calls to the client to obtain compilation information about the compiling method and related classes. Upon instantiation of a `TR_ResolvedJ9JITServerMethod` class, a mirror TR_ResolvedJ9Method will be created on the client via `TR_ResolvedJ9JITServerMethod::createResolvedMethodMirror()`. During compilation the JITServer will mostly be communicating with the client-side mirror instance. Certain data are cached at the JITServer to reduce the number of remote calls to JITClient. - Introduce class `TR_ResolvedRelocatableJ9JITServerMethod`. It is a class used by JITServer for obtaining method/class information needed during compilation from JITClient plus additional handling for AOT compilations. This class is an extension of the above TR_ResolvedJ9JITServerMethod class that has additional handling for AOT compilations. The relationship between TR_ResolvedJ9JITServerMethod and TR_ResolvedRelocatableJ9JITServerMethod is similar to the relationship between TR_ResolvedJ9Method and TR_ResolvedRelocatableJ9Method. - One JITSERVER_TODO used for `TR_J9ServerVM` which is not in mainline yet - Remove JITSERVER_TODO in JITServerIProfiler.cpp that previously depend on `j9methodServer.hpp` - Introduce `J9::Recompilation::persistentJittedBodyInfoFromString`, JITServer only Signed-off-by: Harry Yu --- runtime/compiler/build/files/common.mk | 1 + runtime/compiler/control/J9Recompilation.cpp | 23 + runtime/compiler/control/J9Recompilation.hpp | 6 +- .../control/JITServerCompilationThread.cpp | 209 +- .../control/JITServerCompilationThread.hpp | 16 +- runtime/compiler/control/JITServerHelpers.cpp | 11 - .../compiler/control/RecompilationInfo.hpp | 1 + runtime/compiler/env/CMakeLists.txt | 8 +- runtime/compiler/env/j9methodServer.cpp | 2614 +++++++++++++++++ runtime/compiler/env/j9methodServer.hpp | 315 ++ runtime/compiler/runtime/IProfiler.hpp | 1 + runtime/compiler/runtime/JITClientSession.cpp | 96 +- runtime/compiler/runtime/JITClientSession.hpp | 25 +- .../compiler/runtime/JITServerIProfiler.cpp | 3 +- runtime/compiler/x/runtime/Recomp.cpp | 12 + 15 files changed, 3248 insertions(+), 93 deletions(-) create mode 100644 runtime/compiler/env/j9methodServer.cpp create mode 100644 runtime/compiler/env/j9methodServer.hpp diff --git a/runtime/compiler/build/files/common.mk b/runtime/compiler/build/files/common.mk index 9f612a7f43..6293a749e2 100644 --- a/runtime/compiler/build/files/common.mk +++ b/runtime/compiler/build/files/common.mk @@ -385,6 +385,7 @@ ifneq ($(JITSERVER_SUPPORT),) JIT_PRODUCT_SOURCE_FILES+=\ compiler/control/JITServerCompilationThread.cpp \ compiler/control/JITServerHelpers.cpp \ + compiler/env/j9methodServer.cpp \ compiler/net/ClientStream.cpp \ compiler/net/CommunicationStream.cpp \ compiler/net/ProtobufTypeConvert.cpp \ diff --git a/runtime/compiler/control/J9Recompilation.cpp b/runtime/compiler/control/J9Recompilation.cpp index 2e364a9a63..4ccf3a0286 100644 --- a/runtime/compiler/control/J9Recompilation.cpp +++ b/runtime/compiler/control/J9Recompilation.cpp @@ -31,6 +31,9 @@ #include "env/VMJ9.h" #include "runtime/J9Profiler.hpp" #include "exceptions/RuntimeFailure.hpp" +#if defined(JITSERVER_SUPPORT) +#include "control/JITServerCompilationThread.hpp" +#endif bool J9::Recompilation::_countingSupported = false; @@ -717,3 +720,23 @@ TR_PersistentMethodInfo::setForSharedInfo(TR_PersistentProfileInfo** ptr, TR_Per if (oldPtr) TR_PersistentProfileInfo::decRefCount((TR_PersistentProfileInfo*)oldPtr); } + +#if defined(JITSERVER_SUPPORT) +TR_PersistentJittedBodyInfo * +J9::Recompilation::persistentJittedBodyInfoFromString(const std::string &bodyInfoStr, const std::string &methodInfoStr, TR_Memory *trMemory) + { + auto bodyInfo = (TR_PersistentJittedBodyInfo*) trMemory->allocateHeapMemory(sizeof(TR_PersistentJittedBodyInfo), TR_MemoryBase::Recompilation); + auto methodInfo = (TR_PersistentMethodInfo*) trMemory->allocateHeapMemory(sizeof(TR_PersistentMethodInfo), TR_MemoryBase::Recompilation); + memcpy(bodyInfo, &bodyInfoStr[0], sizeof(TR_PersistentJittedBodyInfo)); + memcpy(methodInfo, &methodInfoStr[0], sizeof(TR_PersistentMethodInfo)); + bodyInfo->setMethodInfo(methodInfo); + bodyInfo->setProfileInfo(NULL); + bodyInfo->setMapTable(NULL); + methodInfo->setOptimizationPlan(NULL); + // cannot use setter because it calls the destructor on the old profile data, + // which is a client pointer + methodInfo->_recentProfileInfo = NULL; + methodInfo->_bestProfileInfo = NULL; + return bodyInfo; + } +#endif diff --git a/runtime/compiler/control/J9Recompilation.hpp b/runtime/compiler/control/J9Recompilation.hpp index 609ed08a8a..a11b786530 100644 --- a/runtime/compiler/control/J9Recompilation.hpp +++ b/runtime/compiler/control/J9Recompilation.hpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corp. and others + * Copyright (c) 2000, 2019 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -130,6 +130,10 @@ class Recompilation : public OMR::RecompilationConnector static int32_t jitGlobalSampleCount; static int32_t jitRecompilationsInduced; +#if defined(JITSERVER_SUPPORT) + static TR_PersistentJittedBodyInfo * persistentJittedBodyInfoFromString(const std::string &bodyInfoStr, const std::string &methodInfoStr, TR_Memory * trMemory); +#endif + protected: virtual TR_PersistentMethodInfo *getExistingMethodInfo(TR_ResolvedMethod *method); diff --git a/runtime/compiler/control/JITServerCompilationThread.cpp b/runtime/compiler/control/JITServerCompilationThread.cpp index 9a1285ca98..a9a4fba999 100644 --- a/runtime/compiler/control/JITServerCompilationThread.cpp +++ b/runtime/compiler/control/JITServerCompilationThread.cpp @@ -49,7 +49,7 @@ TR::CompilationInfoPerThreadRemote::CompilationInfoPerThreadRemote(TR::Compilati {} /** - * Code to be executed on the JITServer to store bytecode iprofiler info to heap memory (instead of to persistent memory) + * @brief Method executed by JITServer to store bytecode iprofiler info to heap memory (instead of to persistent memory) * * @param method J9Method in question * @param byteCodeIndex bytecode in question @@ -84,7 +84,7 @@ TR::CompilationInfoPerThreadRemote::cacheIProfilerInfo(TR_OpaqueMethodBlock *met } /** - * Code to be executed on the JITServer to retrieve bytecode iprofiler info from the heap memory + * @brief Method executed by JITServer to retrieve bytecode iprofiler info from the heap memory * * @param method J9Method in question * @param byteCodeIndex bytecode in question @@ -110,6 +110,119 @@ TR::CompilationInfoPerThreadRemote::getCachedIProfilerInfo(TR_OpaqueMethodBlock return ipEntry; } +/** + * @brief Method executed by JITServer to cache a resolved method to the resolved method cache + * + * @param key Identifier used to identify a resolved method in resolved methods cache + * @param method The resolved method of interest + * @param vTableSlot The vTableSlot for the resolved method of interest + * @param methodInfo Additional method info about the resolved method of interest + * @return returns void + */ +void +TR::CompilationInfoPerThreadRemote::cacheResolvedMethod(TR_ResolvedMethodKey key, TR_OpaqueMethodBlock *method, uint32_t vTableSlot, TR_ResolvedJ9JITServerMethodInfo &methodInfo) + { + static bool useCaching = !feGetEnv("TR_DisableResolvedMethodsCaching"); + if (!useCaching) + return; + + cacheToPerCompilationMap(_resolvedMethodInfoMap, key, {method, vTableSlot, methodInfo}); + } + +/** + * @brief Method executed by JITServer to retrieve a resolved method from the resolved method cache + * + * @param key Identifier used to identify a resolved method in resolved methods cache + * @param owningMethod Owning method of the resolved method of interest + * @param resolvedMethod The resolved method of interest, set by this API + * @param unresolvedInCP The unresolvedInCP boolean value of interest, set by this API + * @return returns true if method is cached, sets resolvedMethod and unresolvedInCP to cached values, false otherwise. + */ +bool +TR::CompilationInfoPerThreadRemote::getCachedResolvedMethod(TR_ResolvedMethodKey key, TR_ResolvedJ9JITServerMethod *owningMethod, TR_ResolvedMethod **resolvedMethod, bool *unresolvedInCP) + { + TR_ResolvedMethodCacheEntry methodCacheEntry; + if (getCachedValueFromPerCompilationMap(_resolvedMethodInfoMap, key, methodCacheEntry)) + { + auto comp = getCompilation(); + TR_OpaqueMethodBlock *method = methodCacheEntry.method; + if (!method) + { + *resolvedMethod = NULL; + return true; + } + auto methodInfo = methodCacheEntry.methodInfo; + uint32_t vTableSlot = methodCacheEntry.vTableSlot; + + + // Re-add validation record + if (comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager) && !comp->getSymbolValidationManager()->inHeuristicRegion()) + { + if(!owningMethod->addValidationRecordForCachedResolvedMethod(key, method)) + { + // Could not add a validation record + *resolvedMethod = NULL; + if (unresolvedInCP) + *unresolvedInCP = true; + return true; + } + } + // Create resolved method from cached method info + if (key.type != TR_ResolvedMethodType::VirtualFromOffset) + { + *resolvedMethod = owningMethod->createResolvedMethodFromJ9Method( + comp, + key.cpIndex, + vTableSlot, + (J9Method *) method, + unresolvedInCP, + NULL, + methodInfo); + } + else + { + if (_vm->isAOT_DEPRECATED_DO_NOT_USE()) + *resolvedMethod = method ? new (comp->trHeapMemory()) TR_ResolvedRelocatableJ9JITServerMethod(method, _vm, comp->trMemory(), methodInfo, owningMethod) : 0; + else + *resolvedMethod = method ? new (comp->trHeapMemory()) TR_ResolvedJ9JITServerMethod(method, _vm, comp->trMemory(), methodInfo, owningMethod) : 0; + } + + if (*resolvedMethod) + { + if (unresolvedInCP) + *unresolvedInCP = false; + return true; + } + else + { + TR_ASSERT(false, "Should not have cached unresolved method globally"); + } + } + return false; + } + +/** + * @brief Method executed by JITServer to compose a TR_ResolvedMethodKey used for the resolved method cache + * + * @param type Resolved method type: VirtualFromCP, VirtualFromOffset, Interface, Static, Special, ImproperInterface, NoType + * @param ramClass ramClass of resolved method of interest + * @param cpIndex constant pool index + * @param classObject default to NULL, only set for resolved interface method + * @return key used to identify a resolved method in the resolved method cache + */ +TR_ResolvedMethodKey +TR::CompilationInfoPerThreadRemote::getResolvedMethodKey(TR_ResolvedMethodType type, TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_OpaqueClassBlock *classObject) + { + TR_ResolvedMethodKey key = {type, ramClass, cpIndex, classObject}; + return key; + } + +/** + * @brief Method executed by JITServer to save the mirrors of resolved method of interest to a list + * + * @param resolvedMethod The mirror of the resolved method of interest (existing at JITClient) + * @return void + */ void TR::CompilationInfoPerThreadRemote::cacheResolvedMirrorMethodsPersistIPInfo(TR_ResolvedJ9Method *resolvedMethod) { @@ -122,3 +235,95 @@ TR::CompilationInfoPerThreadRemote::cacheResolvedMirrorMethodsPersistIPInfo(TR_R _resolvedMirrorMethodsPersistIPInfo->push_back(resolvedMethod); } + +/** + * @brief Method executed by JITServer to remember NULL answers for classOfStatic() queries + * + * @param ramClass The static class of interest as part of the key + * @param cpIndex The constant pool index of interest as part of the key + * @return void + */ +void +TR::CompilationInfoPerThreadRemote::cacheNullClassOfStatic(TR_OpaqueClassBlock *ramClass, int32_t cpIndex) + { + TR_OpaqueClassBlock *nullClazz = NULL; + cacheToPerCompilationMap(_classOfStaticMap, std::make_pair(ramClass, cpIndex), nullClazz); + } + +/** + * @brief Method executed by JITServer to determine if a previous classOfStatic() query returned NULL + * + * @param ramClass The static class of interest + * @param cpIndex The constant pool index of interest + * @return returns true if the previous classOfStatic() query returned NULL and false otherwise + */ +bool +TR::CompilationInfoPerThreadRemote::getCachedNullClassOfStatic(TR_OpaqueClassBlock *ramClass, int32_t cpIndex) + { + TR_OpaqueClassBlock *nullClazz; + return getCachedValueFromPerCompilationMap(_classOfStaticMap, std::make_pair(ramClass, cpIndex), nullClazz); + } + +/** + * @brief Method executed by JITServer to cache field or static attributes + * + * @param ramClass The ramClass of interest as part of the key + * @param cpIndex The cpIndex of interest as part of the key + * @param attrs The value we are going to cache + * @param isStatic Whether the field is static + * @return void + */ +void +TR::CompilationInfoPerThreadRemote::cacheFieldOrStaticAttributes(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, const TR_J9MethodFieldAttributes &attrs, bool isStatic) + { + if (isStatic) + cacheToPerCompilationMap(_staticAttributesCache, std::make_pair(ramClass, cpIndex), attrs); + else + cacheToPerCompilationMap(_fieldAttributesCache, std::make_pair(ramClass, cpIndex), attrs); + } + +/** + * @brief Method executed by JITServer to retrieve field or static attributes from the cache + * + * @param ramClass The ramClass of interest as part of the key + * @param cpIndex The cpIndex of interest as part of the value + * @param attrs The value to be set by the API + * @param isStatic Whether the field is static + * @return returns true if found in cache else false + */ +bool +TR::CompilationInfoPerThreadRemote::getCachedFieldOrStaticAttributes(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_J9MethodFieldAttributes &attrs, bool isStatic) + { + if (isStatic) + return getCachedValueFromPerCompilationMap(_staticAttributesCache, std::make_pair(ramClass, cpIndex), attrs); + else + return getCachedValueFromPerCompilationMap(_fieldAttributesCache, std::make_pair(ramClass, cpIndex), attrs); + } + +/** + * @brief Method executed by JITServer to cache unresolved string + * + * @param ramClass The ramClass of interest as part of the key + * @param cpIndex The cpIndex of interest as part of the key + * @param stringAttrs The value we are going to cache + * @return void + */ +void +TR::CompilationInfoPerThreadRemote::cacheIsUnresolvedStr(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, const TR_IsUnresolvedString &stringAttrs) + { + cacheToPerCompilationMap(_isUnresolvedStrCache, std::make_pair(ramClass, cpIndex), stringAttrs); + } + +/** + * @brief Method executed by JITServer to retrieve unresolved string + * + * @param ramClass The ramClass of interest as part of the key + * @param cpIndex The cpIndex of interest as part of the key + * @param attrs The value to be set by the API + * @return returns true if found in cache else false + */ +bool +TR::CompilationInfoPerThreadRemote::getCachedIsUnresolvedStr(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_IsUnresolvedString &stringAttrs) + { + return getCachedValueFromPerCompilationMap(_isUnresolvedStrCache, std::make_pair(ramClass, cpIndex), stringAttrs); + } diff --git a/runtime/compiler/control/JITServerCompilationThread.hpp b/runtime/compiler/control/JITServerCompilationThread.hpp index 7cb8ef9e0b..70252b825a 100644 --- a/runtime/compiler/control/JITServerCompilationThread.hpp +++ b/runtime/compiler/control/JITServerCompilationThread.hpp @@ -24,11 +24,10 @@ #define JITSERVER_COMPILATION_THREAD_H #include "control/CompilationThread.hpp" +#include "env/j9methodServer.hpp" #include "runtime/JITClientSession.hpp" class TR_IPBytecodeHashTableEntry; -class TR_ResolvedMethodInfoCache; -class TR_IsUnresolvedString; using IPTableHeapEntry = UnorderedMap; using IPTableHeap_t = UnorderedMap; @@ -50,9 +49,22 @@ class CompilationInfoPerThreadRemote : public TR::CompilationInfoPerThread bool cacheIProfilerInfo(TR_OpaqueMethodBlock *method, uint32_t byteCodeIndex, TR_IPBytecodeHashTableEntry *entry); TR_IPBytecodeHashTableEntry *getCachedIProfilerInfo(TR_OpaqueMethodBlock *method, uint32_t byteCodeIndex, bool *methodInfoPresent); + void cacheResolvedMethod(TR_ResolvedMethodKey key, TR_OpaqueMethodBlock *method, uint32_t vTableSlot, TR_ResolvedJ9JITServerMethodInfo &methodInfo); + bool getCachedResolvedMethod(TR_ResolvedMethodKey key, TR_ResolvedJ9JITServerMethod *owningMethod, TR_ResolvedMethod **resolvedMethod, bool *unresolvedInCP = NULL); + TR_ResolvedMethodKey getResolvedMethodKey(TR_ResolvedMethodType type, TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_OpaqueClassBlock *classObject = NULL); + void cacheResolvedMirrorMethodsPersistIPInfo(TR_ResolvedJ9Method *resolvedMethod); ResolvedMirrorMethodsPersistIP_t *getCachedResolvedMirrorMethodsPersistIPInfo() const { return _resolvedMirrorMethodsPersistIPInfo; } + void cacheNullClassOfStatic(TR_OpaqueClassBlock *ramClass, int32_t cpIndex); + bool getCachedNullClassOfStatic(TR_OpaqueClassBlock *ramClass, int32_t cpIndex); + + void cacheFieldOrStaticAttributes(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, const TR_J9MethodFieldAttributes &attrs, bool isStatic); + bool getCachedFieldOrStaticAttributes(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_J9MethodFieldAttributes &attrs, bool isStatic); + + void cacheIsUnresolvedStr(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, const TR_IsUnresolvedString &stringAttrs); + bool getCachedIsUnresolvedStr(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_IsUnresolvedString &stringAttrs); + private: /* Template method for allocating a cache of type T on the heap. * Cache pointer must be NULL. diff --git a/runtime/compiler/control/JITServerHelpers.cpp b/runtime/compiler/control/JITServerHelpers.cpp index eab1a0b8fb..264fa78aa1 100644 --- a/runtime/compiler/control/JITServerHelpers.cpp +++ b/runtime/compiler/control/JITServerHelpers.cpp @@ -196,8 +196,6 @@ JITServerHelpers::cacheRemoteROMClass(ClientSessionData *clientSessionData, J9Cl J9Method *methods = classInfoStruct._methodsOfClass; classInfoStruct._baseComponentClass = std::get<2>(classInfo); classInfoStruct._numDimensions = std::get<3>(classInfo); - classInfoStruct._remoteROMStringsCache = NULL; - classInfoStruct._fieldOrStaticNameCache = NULL; classInfoStruct._parentClass = std::get<4>(classInfo); auto &tmpInterfaces = std::get<5>(classInfo); classInfoStruct._interfaces = new (PERSISTENT_NEW) PersistentVector @@ -214,18 +212,9 @@ JITServerHelpers::cacheRemoteROMClass(ClientSessionData *clientSessionData, J9Cl classInfoStruct._componentClass = std::get<14>(classInfo); classInfoStruct._arrayClass = std::get<15>(classInfo); classInfoStruct._totalInstanceSize = std::get<16>(classInfo); - classInfoStruct._classOfStaticCache = NULL; - classInfoStruct._constantClassPoolCache = NULL; classInfoStruct._remoteRomClass = std::get<17>(classInfo); - classInfoStruct._fieldAttributesCache = NULL; - classInfoStruct._staticAttributesCache = NULL; - classInfoStruct._fieldAttributesCacheAOT = NULL; - classInfoStruct._staticAttributesCacheAOT = NULL; classInfoStruct._constantPool = (J9ConstantPool *)std::get<18>(classInfo); - classInfoStruct._jitFieldsCache = NULL; classInfoStruct._classFlags = std::get<19>(classInfo); - classInfoStruct._fieldOrStaticDeclaringClassCache = NULL; - classInfoStruct._J9MethodNameCache = NULL; clientSessionData->getROMClassMap().insert({ clazz, classInfoStruct}); uint32_t numMethods = romClass->romMethodCount; diff --git a/runtime/compiler/control/RecompilationInfo.hpp b/runtime/compiler/control/RecompilationInfo.hpp index 605c975bcf..e4c69279ed 100644 --- a/runtime/compiler/control/RecompilationInfo.hpp +++ b/runtime/compiler/control/RecompilationInfo.hpp @@ -171,6 +171,7 @@ class TR_PersistentMethodInfo uint16_t getTimeStamp() { return _timeStamp; } TR_OptimizationPlan * getOptimizationPlan() {return _optimizationPlan;} + void setOptimizationPlan(TR_OptimizationPlan *optPlan) { _optimizationPlan = optPlan; } uint8_t getNumberOfInvalidations() {return _numberOfInvalidations;} void incrementNumberOfInvalidations() {_numberOfInvalidations++;} uint8_t getNumberOfInlinedMethodRedefinition() {return _numberOfInlinedMethodRedefinition;} diff --git a/runtime/compiler/env/CMakeLists.txt b/runtime/compiler/env/CMakeLists.txt index f0ff961b9f..f85a64b8e9 100644 --- a/runtime/compiler/env/CMakeLists.txt +++ b/runtime/compiler/env/CMakeLists.txt @@ -1,5 +1,5 @@ ################################################################################ -# Copyright (c) 2017, 2018 IBM Corp. and others +# Copyright (c) 2017, 2019 IBM Corp. and others # # This program and the accompanying materials are made available under # the terms of the Eclipse Public License 2.0 which accompanies this @@ -58,3 +58,9 @@ j9jit_files( env/SystemSegmentProvider.cpp env/VMJ9.cpp ) + +if(JITSERVER_SUPPORT) + j9jit_files( + env/j9methodServer.cpp + ) +endif() diff --git a/runtime/compiler/env/j9methodServer.cpp b/runtime/compiler/env/j9methodServer.cpp new file mode 100644 index 0000000000..9d0f54a111 --- /dev/null +++ b/runtime/compiler/env/j9methodServer.cpp @@ -0,0 +1,2614 @@ +/******************************************************************************* + * Copyright (c) 2018, 2019 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * 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 + *******************************************************************************/ + +#include "net/ServerStream.hpp" +#include "j9methodServer.hpp" +#include "control/CompilationRuntime.hpp" +#include "control/CompilationThread.hpp" +#include "control/MethodToBeCompiled.hpp" +#include "control/JITServerCompilationThread.hpp" +#include "control/JITServerHelpers.hpp" +#include "exceptions/DataCacheError.hpp" +#include "ilgen/J9ByteCodeIterator.hpp" + +ClientSessionData::ClassInfo & +getJ9ClassInfo(TR::CompilationInfoPerThread *threadCompInfo, J9Class *clazz) + { + // This function assumes that you are inside of _romMapMonitor + // Do not use it otherwise + auto &classMap = threadCompInfo->getClientData()->getROMClassMap(); + auto it = classMap.find(clazz); + TR_ASSERT(it != classMap.end(), "ClassInfo is not in the class map"); + return it->second; + } + +static J9ROMMethod * +romMethodAtClassIndex(J9ROMClass *romClass, uint64_t methodIndex) + { + J9ROMMethod * romMethod = J9ROMCLASS_ROMMETHODS(romClass); + for (size_t i = methodIndex; i; --i) + romMethod = nextROMMethod(romMethod); + return romMethod; + } + +TR_ResolvedJ9JITServerMethod::TR_ResolvedJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd * fe, TR_Memory * trMemory, TR_ResolvedMethod * owningMethod, uint32_t vTableSlot) + : TR_ResolvedJ9Method(fe, owningMethod) + { + TR_J9VMBase *fej9 = (TR_J9VMBase *)fe; + TR::CompilationInfo *compInfo = TR::CompilationInfo::get(fej9->getJ9JITConfig()); + TR::CompilationInfoPerThread *threadCompInfo = compInfo->getCompInfoForThread(fej9->vmThread()); + _stream = threadCompInfo->getMethodBeingCompiled()->_stream; + + // Create client side mirror of this object to use for calls involving RAM data + TR_ResolvedJ9Method* owningMethodMirror = owningMethod ? ((TR_ResolvedJ9JITServerMethod*) owningMethod)->_remoteMirror : NULL; + + // If in AOT mode, will actually create relocatable version of resolved method on the client + _stream->write(JITServer::MessageType::mirrorResolvedJ9Method, aMethod, owningMethodMirror, vTableSlot, fej9->isAOT_DEPRECATED_DO_NOT_USE()); + auto recv = _stream->read(); + auto &methodInfo = std::get<0>(recv); + + unpackMethodInfo(aMethod, fe, trMemory, vTableSlot, threadCompInfo, methodInfo); + } + +TR_ResolvedJ9JITServerMethod::TR_ResolvedJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd * fe, TR_Memory * trMemory, const TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_ResolvedMethod * owningMethod, uint32_t vTableSlot) + : TR_ResolvedJ9Method(fe, owningMethod) + { + // Mirror has already been created, its parameters are passed in methodInfo + TR_J9VMBase *fej9 = (TR_J9VMBase *)fe; + TR::CompilationInfo *compInfo = TR::CompilationInfo::get(fej9->getJ9JITConfig()); + TR::CompilationInfoPerThread *threadCompInfo = compInfo->getCompInfoForThread(fej9->vmThread()); + _stream = threadCompInfo->getMethodBeingCompiled()->_stream; + unpackMethodInfo(aMethod, fe, trMemory, vTableSlot, threadCompInfo, methodInfo); + } + +J9ROMClass * +TR_ResolvedJ9JITServerMethod::romClassPtr() + { + return _romClass; + } + +J9RAMConstantPoolItem * +TR_ResolvedJ9JITServerMethod::literals() + { + return _literals; + } + +J9Class * +TR_ResolvedJ9JITServerMethod::constantPoolHdr() + { + return _ramClass; + } + +bool +TR_ResolvedJ9JITServerMethod::isJNINative() + { + return _isJNINative; + } + +void +TR_ResolvedJ9JITServerMethod::setRecognizedMethodInfo(TR::RecognizedMethod rm) + { + TR_ResolvedJ9Method::setRecognizedMethodInfo(rm); + _stream->write(JITServer::MessageType::ResolvedMethod_setRecognizedMethodInfo, _remoteMirror, rm); + _stream->read(); + } + +J9ClassLoader * +TR_ResolvedJ9JITServerMethod::getClassLoader() + { + return _classLoader; // cached version + } + +bool +TR_ResolvedJ9JITServerMethod::staticAttributes(TR::Compilation * comp, I_32 cpIndex, void * * address, TR::DataType * type, bool * volatileP, bool * isFinal, bool * isPrivate, bool isStore, bool * unresolvedInCP, bool needAOTValidation) + { + bool isStatic = true; + TR_J9MethodFieldAttributes attributes; + if(!getCachedFieldAttributes(cpIndex, attributes, isStatic)) + { + // make a remote call, if attributes are not cached + _stream->write(JITServer::MessageType::ResolvedMethod_staticAttributes, _remoteMirror, cpIndex, isStore, needAOTValidation); + auto recv = _stream->read(); + attributes = std::get<0>(recv); + + cacheFieldAttributes(cpIndex, attributes, isStatic); + } + else + { + TR_ASSERT(validateMethodFieldAttributes(attributes, isStatic, cpIndex, isStore, needAOTValidation), "static attributes from client and cache do not match"); + } + + bool result; + attributes.setMethodFieldAttributesResult(address, type, volatileP, isFinal, isPrivate, unresolvedInCP, &result); + + return result; + } + +TR_OpaqueClassBlock * +TR_ResolvedJ9JITServerMethod::getClassFromConstantPool(TR::Compilation * comp, uint32_t cpIndex, bool returnClassForAOT) + { + if (cpIndex == -1) + return NULL; + + TR::CompilationInfoPerThread *compInfoPT = _fe->_compInfoPT; + { + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &constantClassPoolCache = getJ9ClassInfo(compInfoPT, _ramClass)._constantClassPoolCache; + auto it = constantClassPoolCache.find(cpIndex); + if (it != constantClassPoolCache.end()) + return it->second; + } + _stream->write(JITServer::MessageType::ResolvedMethod_getClassFromConstantPool, _remoteMirror, cpIndex, returnClassForAOT); + TR_OpaqueClassBlock *resolvedClass = std::get<0>(_stream->read()); + if (resolvedClass) + { + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &constantClassPoolCache = getJ9ClassInfo(compInfoPT, _ramClass)._constantClassPoolCache; + constantClassPoolCache.insert({cpIndex, resolvedClass}); + } + return resolvedClass; + } + +TR_OpaqueClassBlock * +TR_ResolvedJ9JITServerMethod::getDeclaringClassFromFieldOrStatic(TR::Compilation *comp, int32_t cpIndex) + { + TR::CompilationInfoPerThread *compInfoPT = _fe->_compInfoPT; + { + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &cache = getJ9ClassInfo(compInfoPT, _ramClass)._fieldOrStaticDeclaringClassCache; + auto it = cache.find(cpIndex); + if (it != cache.end()) + return it->second; + } + _stream->write(JITServer::MessageType::ResolvedMethod_getDeclaringClassFromFieldOrStatic, _remoteMirror, cpIndex); + TR_OpaqueClassBlock *declaringClass = std::get<0>(_stream->read()); + if (declaringClass) + { + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &cache = getJ9ClassInfo(compInfoPT, _ramClass)._fieldOrStaticDeclaringClassCache; + cache.insert({cpIndex, declaringClass}); + } + return declaringClass; + } + +TR_OpaqueClassBlock * +TR_ResolvedJ9JITServerMethod::classOfStatic(I_32 cpIndex, bool returnClassForAOT) + { + // if method is unresolved, return right away + if (cpIndex < 0) + return NULL; + + auto compInfoPT = static_cast(_fe->_compInfoPT); + { + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &classOfStaticCache = getJ9ClassInfo(compInfoPT, _ramClass)._classOfStaticCache; + auto it = classOfStaticCache.find(cpIndex); + if (it != classOfStaticCache.end()) + return it->second; + } + + // check per-compilation cache, which can only contain NULL values + if (compInfoPT->getCachedNullClassOfStatic((TR_OpaqueClassBlock *) _ramClass, cpIndex)) + return NULL; + + _stream->write(JITServer::MessageType::ResolvedMethod_classOfStatic, _remoteMirror, cpIndex, returnClassForAOT); + TR_OpaqueClassBlock *classOfStatic = std::get<0>(_stream->read()); + if (classOfStatic) + { + // reacquire monitor and cache, if client returned a valid class + // if client returned NULL, don't cache, because class might not be fully initialized, + // so the result may change in the future + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &classOfStaticCache = getJ9ClassInfo(compInfoPT, _ramClass)._classOfStaticCache; + classOfStaticCache.insert({cpIndex, classOfStatic}); + } + else + { + // cache NULL in a per-compilation cache, even if it becomes initialized during + // current compilation, shouldn't have much of an effect on performance. + compInfoPT->cacheNullClassOfStatic((TR_OpaqueClassBlock *) _ramClass, cpIndex); + } + return classOfStatic; + } + +bool +TR_ResolvedJ9JITServerMethod::isConstantDynamic(I_32 cpIndex) + { + TR_ASSERT_FATAL(cpIndex != -1, "ConstantDynamic cpIndex shouldn't be -1"); + UDATA cpType = J9_CP_TYPE(J9ROMCLASS_CPSHAPEDESCRIPTION(_romClass), cpIndex); + return (J9CPTYPE_CONSTANT_DYNAMIC == cpType); + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedPossiblyPrivateVirtualMethod(TR::Compilation * comp, I_32 cpIndex, bool ignoreRtResolve, bool * unresolvedInCP) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); +#if TURN_OFF_INLINING + return 0; +#else + auto compInfoPT = (TR::CompilationInfoPerThreadRemote *) _fe->_compInfoPT; + TR_ResolvedMethod *resolvedMethod = NULL; + if (compInfoPT->getCachedResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::VirtualFromCP, (TR_OpaqueClassBlock *) _ramClass, cpIndex), this, &resolvedMethod, unresolvedInCP)) + return resolvedMethod; + + // See if the constant pool entry is already resolved or not + if (unresolvedInCP) + *unresolvedInCP = true; + + if (!((_fe->_compInfoPT->getClientData()->getRtResolve()) && + !comp->ilGenRequest().details().isMethodHandleThunk() && // cmvc 195373 + performTransformation(comp, "Setting as unresolved virtual call cpIndex=%d\n",cpIndex) ) || ignoreRtResolve) + { + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedPossiblyPrivateVirtualMethodAndMirror, (TR_ResolvedMethod *) _remoteMirror, literals(), cpIndex); + auto recv = _stream->read(); + J9Method *ramMethod = std::get<0>(recv); + UDATA vTableIndex = std::get<1>(recv); + + if (std::get<2>(recv) && unresolvedInCP) + *unresolvedInCP = false; + + bool createResolvedMethod = true; + + if (comp->compileRelocatableCode() && ramMethod && comp->getOption(TR_UseSymbolValidationManager)) + { + if (!comp->getSymbolValidationManager()->addVirtualMethodFromCPRecord((TR_OpaqueMethodBlock *)ramMethod, cp(), cpIndex)) + createResolvedMethod = false; + } + + if (vTableIndex) + { + TR_AOTInliningStats *aotStats = NULL; + if (comp->getOption(TR_EnableAOTStats)) + aotStats = & (((TR_JitPrivateConfig *)_fe->_jitConfig->privateConfig)->aotStats->virtualMethods); + + TR_ResolvedJ9JITServerMethodInfo methodInfo = std::get<3>(recv); + + // call constructor without making a new query + if (createResolvedMethod) + { + resolvedMethod = createResolvedMethodFromJ9Method(comp, cpIndex, vTableIndex, ramMethod, unresolvedInCP, aotStats, methodInfo); + compInfoPT->cacheResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::VirtualFromCP, (TR_OpaqueClassBlock *) _ramClass, cpIndex), (TR_OpaqueMethodBlock *) ramMethod, (uint32_t) vTableIndex, methodInfo); + } + } + } + + if (resolvedMethod == NULL) + { + TR::DebugCounter::incStaticDebugCounter(comp, "resources.resolvedMethods/virtual/null"); + if (unresolvedInCP) + handleUnresolvedVirtualMethodInCP(cpIndex, unresolvedInCP); + } + else + { + if (((TR_ResolvedJ9Method*)resolvedMethod)->isVarHandleAccessMethod()) + { + // VarHandle access methods are resolved to *_impl()V, restore their signatures to obtain function correctness in the Walker + J9ROMConstantPoolItem *cpItem = (J9ROMConstantPoolItem *)romLiterals(); + J9ROMMethodRef *romMethodRef = (J9ROMMethodRef *)(cpItem + cpIndex); + J9ROMNameAndSignature *nameAndSig = J9ROMFIELDREF_NAMEANDSIGNATURE(romMethodRef); + int32_t signatureLength; + char *signature = utf8Data(J9ROMNAMEANDSIGNATURE_SIGNATURE(nameAndSig), signatureLength); + ((TR_ResolvedJ9Method *)resolvedMethod)->setSignature(signature, signatureLength, comp->trMemory()); + } + + TR::DebugCounter::incStaticDebugCounter(comp, "resources.resolvedMethods/virtual"); + TR::DebugCounter::incStaticDebugCounter(comp, "resources.resolvedMethods/virtual:#bytes", sizeof(TR_ResolvedJ9Method)); + + } + + return resolvedMethod; +#endif + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedVirtualMethod( + TR::Compilation *comp, + I_32 cpIndex, + bool ignoreRtResolve, + bool *unresolvedInCP) + { + TR_ResolvedMethod *method = getResolvedPossiblyPrivateVirtualMethod( + comp, + cpIndex, + ignoreRtResolve, + unresolvedInCP); + + return (method == NULL || method->isPrivate()) ? NULL : method; + } + +bool +TR_ResolvedJ9JITServerMethod::fieldsAreSame(int32_t cpIndex1, TR_ResolvedMethod *m2, int32_t cpIndex2, bool &sigSame) + { + if (TR::comp()->compileRelocatableCode()) + // in AOT, this should always return false, because mainline compares class loaders + // with fe->sameClassLoaders, which always returns false for AOT compiles + return false; + TR_ResolvedJ9JITServerMethod *serverMethod2 = static_cast(m2); + if (getClassLoader() != serverMethod2->getClassLoader()) + return false; + + if (cpIndex1 == -1 || cpIndex2 == -1) + return false; + + if (cpIndex1 == cpIndex2 && ramMethod() == serverMethod2->ramMethod()) + return true; + + int32_t sig1Len = 0, sig2Len = 0; + char *signature1 = fieldOrStaticSignatureChars(cpIndex1, sig1Len); + char *signature2 = serverMethod2->fieldOrStaticSignatureChars(cpIndex2, sig2Len); + + int32_t name1Len = 0, name2Len = 0; + char *name1 = fieldOrStaticNameChars(cpIndex1, name1Len); + char *name2 = serverMethod2->fieldOrStaticNameChars(cpIndex2, name2Len); + + if (sig1Len == sig2Len && !memcmp(signature1, signature2, sig1Len) && + name1Len == name2Len && !memcmp(name1, name2, name1Len)) + { + int32_t class1Len = 0, class2Len = 0; + char *declaringClassName1 = classNameOfFieldOrStatic(cpIndex1, class1Len); + char *declaringClassName2 = serverMethod2->classNameOfFieldOrStatic(cpIndex2, class2Len); + + if (class1Len == class2Len && !memcmp(declaringClassName1, declaringClassName2, class1Len)) + return true; + } + else + { + sigSame = false; + } + return false; + } + +bool +TR_ResolvedJ9JITServerMethod::staticsAreSame(int32_t cpIndex1, TR_ResolvedMethod *m2, int32_t cpIndex2, bool &sigSame) + { + if (TR::comp()->compileRelocatableCode()) + // in AOT, this should always return false, because mainline compares class loaders + // with fe->sameClassLoaders, which always returns false for AOT compiles + return false; + TR_ResolvedJ9JITServerMethod *serverMethod2 = static_cast(m2); + if (getClassLoader() != serverMethod2->getClassLoader()) + return false; + + if (cpIndex1 == -1 || cpIndex2 == -1) + return false; + + if (cpIndex1 == cpIndex2 && ramMethod() == serverMethod2->ramMethod()) + return true; + + // the client version of this method compares the addresses of values first, which we cannot do on the server. + // skipping that step affects performance, but does not affect correctness. + int32_t sig1Len = 0, sig2Len = 0; + char *signature1 = fieldOrStaticSignatureChars(cpIndex1, sig1Len); + char *signature2 = serverMethod2->fieldOrStaticSignatureChars(cpIndex2, sig2Len); + + int32_t name1Len = 0, name2Len = 0; + char *name1 = fieldOrStaticNameChars(cpIndex1, name1Len); + char *name2 = serverMethod2->fieldOrStaticNameChars(cpIndex2, name2Len); + + if (sig1Len == sig2Len && !memcmp(signature1, signature2, sig1Len) && + name1Len == name2Len && !memcmp(name1, name2, name1Len)) + { + int32_t class1Len = 0, class2Len = 0; + char *declaringClassName1 = classNameOfFieldOrStatic(cpIndex1, class1Len); + char *declaringClassName2 = serverMethod2->classNameOfFieldOrStatic(cpIndex2, class2Len); + + if (class1Len == class2Len && !memcmp(declaringClassName1, declaringClassName2, class1Len)) + return true; + } + else + { + sigSame = false; + } + return false; + } + +TR_FieldAttributesCache & +TR_ResolvedJ9JITServerMethod::getAttributesCache(bool isStatic, bool unresolvedInCP) + { + // Return a persistent attributes cache for regular JIT compilations + TR::CompilationInfoPerThread *compInfoPT = _fe->_compInfoPT; + auto &attributesCache = isStatic ? + getJ9ClassInfo(compInfoPT, _ramClass)._staticAttributesCache : + getJ9ClassInfo(compInfoPT, _ramClass)._fieldAttributesCache; + return attributesCache; + } + +bool +TR_ResolvedJ9JITServerMethod::getCachedFieldAttributes(int32_t cpIndex, TR_J9MethodFieldAttributes &attributes, bool isStatic) + { + auto compInfoPT = static_cast(_fe->_compInfoPT); + { + // First, search a global cache + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &attributesCache = getAttributesCache(isStatic); + auto it = attributesCache.find(cpIndex); + if (it != attributesCache.end()) + { + attributes = it->second; + return true; + } + } + + // If global cache is empty, search local cache + // Local cache is searched after global, because it only stores + // unresolved field attributes, so most attributes should be stored globally + return compInfoPT->getCachedFieldOrStaticAttributes((TR_OpaqueClassBlock *) _ramClass, cpIndex, attributes, isStatic); + } + +void +TR_ResolvedJ9JITServerMethod::cacheFieldAttributes(int32_t cpIndex, const TR_J9MethodFieldAttributes &attributes, bool isStatic) + { + // When caching field or static attributes, there are 2 caches where they can go: + // 1. Persistent cache - stores attributes of all resolved fields. Since the field is resolved, + // the attributes should not change, so the contents should be valid. + // 2. Per-compilation cache - stores attributes of unresolved fields, which might become resolved + // during the current compilation, but assuming they are unresolved until the end of the compilation + // is still functionally correct and does not seem to affect performance. + auto compInfoPT = static_cast(_fe->_compInfoPT); + if (attributes.isUnresolvedInCP()) + { + // field is unresolved in CP, can later become resolved, can only be cached per resolved method. + compInfoPT->cacheFieldOrStaticAttributes((TR_OpaqueClassBlock *) _ramClass, cpIndex, attributes, isStatic); + } + else + { + // field is resolved in CP, can cache globally per RAM class. + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &attributesCache = getAttributesCache(isStatic); +#if defined(DEBUG) || defined(PROD_WITH_ASSUMES) + TR_ASSERT(canCacheFieldAttributes(cpIndex, attributes, isStatic), "new and cached field attributes are not equal"); +#endif + attributesCache.insert({cpIndex, attributes}); + } + } + +bool +TR_ResolvedJ9JITServerMethod::canCacheFieldAttributes(int32_t cpIndex, const TR_J9MethodFieldAttributes &attributes, bool isStatic) + { + auto &attributesCache = getAttributesCache(isStatic); + auto it = attributesCache.find(cpIndex); + if (it != attributesCache.end()) + { + // Attempting to cache attributes when this key is already cached. + // This case can happen when two threads call `getCachedFieldAttributes`, + // see that attributes are not cached, and they both make a remote call + // and call this method. + // Make sure that attributes they are caching are the same. + auto cachedAttrs = it->second; + return attributes == cachedAttrs; + } + return true; + } + +bool +TR_ResolvedJ9JITServerMethod::fieldAttributes(TR::Compilation * comp, I_32 cpIndex, U_32 * fieldOffset, TR::DataType * type, bool * volatileP, bool * isFinal, bool * isPrivate, bool isStore, bool * unresolvedInCP, bool needAOTValidation) + { + bool isStatic = false; + TR_J9MethodFieldAttributes attributes; + if(!getCachedFieldAttributes(cpIndex, attributes, isStatic)) + { + // make a remote call, if attributes are not cached + _stream->write(JITServer::MessageType::ResolvedMethod_fieldAttributes, _remoteMirror, cpIndex, isStore, needAOTValidation); + auto recv = _stream->read(); + attributes = std::get<0>(recv); + + cacheFieldAttributes(cpIndex, attributes, isStatic); + } + else + { + TR_ASSERT(validateMethodFieldAttributes(attributes, isStatic, cpIndex, isStore, needAOTValidation), "field attributes from client and cache do not match"); + } + + bool result; + attributes.setMethodFieldAttributesResult(fieldOffset, type, volatileP, isFinal, isPrivate, unresolvedInCP, &result); + return result; + } + +static bool doResolveAtRuntime(J9Method *method, I_32 cpIndex, TR::Compilation *comp) + { + if (!method) + return true; + TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp->fe()); + auto stream = fej9->_compInfoPT->getMethodBeingCompiled()->_stream; + if (method == fej9->_compInfoPT->getClientData()->getOrCacheVMInfo(stream)->_invokeWithArgumentsHelperMethod) + { + // invokeWithArgumentsHelper is a weirdo + if (fej9->isAOT_DEPRECATED_DO_NOT_USE()) + { + comp->failCompilation("invokeWithArgumentsHelper"); + } + else + { + return false; // It is incorrect to try to resolve this + } + } + else if (comp->ilGenRequest().details().isMethodHandleThunk()) // cmvc 195373 + { + return false; + } + else if (fej9->_compInfoPT->getClientData()->getRtResolve()) + { + return performTransformation(comp, "Setting as unresolved static call cpIndex=%d\n", cpIndex); + } + + return false; + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedStaticMethod(TR::Compilation * comp, I_32 cpIndex, bool * unresolvedInCP) + { + // JITServer TODO: Decide whether the counters should be updated on the server or the client + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); + + auto compInfoPT = (TR::CompilationInfoPerThreadRemote *) _fe->_compInfoPT; + TR_ResolvedMethod *resolvedMethod = NULL; + if (compInfoPT->getCachedResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::Static, (TR_OpaqueClassBlock *) _ramClass, cpIndex), this, &resolvedMethod, unresolvedInCP)) + return resolvedMethod; + + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedStaticMethodAndMirror, _remoteMirror, cpIndex); + auto recv = _stream->read(); + J9Method * ramMethod = std::get<0>(recv); + if (unresolvedInCP) + *unresolvedInCP = std::get<2>(recv); + + if (comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager) && ramMethod) + { + if (!comp->getSymbolValidationManager()->addStaticMethodFromCPRecord((TR_OpaqueMethodBlock *)ramMethod, cp(), cpIndex)) + ramMethod = NULL; + } + + bool skipForDebugging = doResolveAtRuntime(ramMethod, cpIndex, comp); + if (isArchetypeSpecimen()) + { + // ILGen macros currently must be resolved for correctness, or else they + // are not recognized and expanded. If we have unresolved calls, we can't + // tell whether they're ilgen macros because the recognized-method system + // only works on resovled methods. + // + if (ramMethod) + skipForDebugging = false; + else + { + comp->failCompilation("Can't compile an archetype specimen with unresolved calls"); + } + } + + auto methodInfo = std::get<1>(recv); + if (ramMethod && !skipForDebugging) + { + TR_AOTInliningStats *aotStats = NULL; + if (comp->getOption(TR_EnableAOTStats)) + aotStats = & (((TR_JitPrivateConfig *)_fe->_jitConfig->privateConfig)->aotStats->staticMethods); + resolvedMethod = createResolvedMethodFromJ9Method(comp, cpIndex, 0, ramMethod, unresolvedInCP, aotStats, methodInfo); + if (unresolvedInCP) + *unresolvedInCP = false; + } + + if (resolvedMethod == NULL) + { + if (unresolvedInCP) + handleUnresolvedStaticMethodInCP(cpIndex, unresolvedInCP); + } + else + { + compInfoPT->cacheResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::Static, (TR_OpaqueClassBlock *) _ramClass, cpIndex), (TR_OpaqueMethodBlock *) ramMethod, 0, methodInfo); + } + + return resolvedMethod; + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedSpecialMethod(TR::Compilation * comp, I_32 cpIndex, bool * unresolvedInCP) + { + // JITServer TODO: Decide whether the counters should be updated on the server or the client + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); + + auto compInfoPT = (TR::CompilationInfoPerThreadRemote *) _fe->_compInfoPT; + TR_ResolvedMethod *resolvedMethod = NULL; + TR_OpaqueClassBlock *clazz = (TR_OpaqueClassBlock *) _ramClass; + if (compInfoPT->getCachedResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::Special, clazz, cpIndex), this, &resolvedMethod, unresolvedInCP)) + return resolvedMethod; + + if (unresolvedInCP) + *unresolvedInCP = true; + + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedSpecialMethodAndMirror, _remoteMirror, cpIndex); + auto recv = _stream->read(); + J9Method * ramMethod = std::get<0>(recv); + auto methodInfo = std::get<1>(recv); + + if (ramMethod) + { + bool createResolvedMethod = true; + if (comp->getOption(TR_UseSymbolValidationManager)) + { + if (!comp->getSymbolValidationManager()->addSpecialMethodFromCPRecord((TR_OpaqueMethodBlock *)ramMethod, cp(), cpIndex)) + createResolvedMethod = false; + } + TR_AOTInliningStats *aotStats = NULL; + if (comp->getOption(TR_EnableAOTStats)) + aotStats = & (((TR_JitPrivateConfig *)_fe->_jitConfig->privateConfig)->aotStats->specialMethods); + if (createResolvedMethod) + resolvedMethod = createResolvedMethodFromJ9Method(comp, cpIndex, 0, ramMethod, unresolvedInCP, aotStats, methodInfo); + if (unresolvedInCP) + *unresolvedInCP = false; + } + + if (resolvedMethod == NULL) + { + if (unresolvedInCP) + handleUnresolvedVirtualMethodInCP(cpIndex, unresolvedInCP); + } + else + { + compInfoPT->cacheResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::Special, clazz, cpIndex), (TR_OpaqueMethodBlock *) ramMethod, 0, methodInfo); + } + + return resolvedMethod; + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::createResolvedMethodFromJ9Method( TR::Compilation *comp, int32_t cpIndex, uint32_t vTableSlot, J9Method *j9Method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats) + { + return new (comp->trHeapMemory()) TR_ResolvedJ9JITServerMethod((TR_OpaqueMethodBlock *) j9Method, _fe, comp->trMemory(), this, vTableSlot); + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::createResolvedMethodFromJ9Method( TR::Compilation *comp, int32_t cpIndex, uint32_t vTableSlot, J9Method *j9Method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats, const TR_ResolvedJ9JITServerMethodInfo &methodInfo) + { + return new (comp->trHeapMemory()) TR_ResolvedJ9JITServerMethod((TR_OpaqueMethodBlock *) j9Method, _fe, comp->trMemory(), methodInfo, this, vTableSlot); + } + +uint32_t +TR_ResolvedJ9JITServerMethod::classCPIndexOfMethod(uint32_t methodCPIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_classCPIndexOfMethod, _remoteMirror, methodCPIndex); + return std::get<0>(_stream->read()); + } + +void * +TR_ResolvedJ9JITServerMethod::startAddressForJittedMethod() + { + // return the cached value if we have any + if (_startAddressForJittedMethod) + { + return _startAddressForJittedMethod; + } + else // Otherwise ask the client for it + { + _stream->write(JITServer::MessageType::ResolvedMethod_startAddressForJittedMethod, _remoteMirror); + return std::get<0>(_stream->read()); + } + } + +char * +TR_ResolvedJ9JITServerMethod::localName(U_32 slotNumber, U_32 bcIndex, I_32 &len, TR_Memory *trMemory) + { + _stream->write(JITServer::MessageType::ResolvedMethod_localName, _remoteMirror, slotNumber, bcIndex); + const std::string nameString = std::get<0>(_stream->read()); + len = nameString.length(); + char *out = (char*) trMemory->allocateHeapMemory(len); + memcpy(out, &nameString[0], len); + return out; + } + +TR_OpaqueClassBlock * +TR_ResolvedJ9JITServerMethod::getResolvedInterfaceMethod(I_32 cpIndex, UDATA *pITableIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedInterfaceMethod_2, _remoteMirror, cpIndex); + auto recv = _stream->read(); + *pITableIndex = std::get<1>(recv); + auto result = std::get<0>(recv); + + auto comp = _fe->_compInfoPT->getCompilation(); + if (comp && comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager)) + { + if (!comp->getSymbolValidationManager()->addClassFromITableIndexCPRecord(result, cp(), cpIndex)) + result = NULL; + } + return result; + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedInterfaceMethod(TR::Compilation * comp, TR_OpaqueClassBlock * classObject, I_32 cpIndex) + { + TR_ResolvedMethod *resolvedMethod = NULL; + auto compInfoPT = (TR::CompilationInfoPerThreadRemote *) _fe->_compInfoPT; + TR_OpaqueClassBlock *clazz = (TR_OpaqueClassBlock *) _ramClass; + if (compInfoPT->getCachedResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::Interface, clazz, cpIndex, classObject), this, &resolvedMethod)) + return resolvedMethod; + + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedInterfaceMethodAndMirror_3, getPersistentIdentifier(), classObject, cpIndex, _remoteMirror); + auto recv = _stream->read(); + bool resolved = std::get<0>(recv); + J9Method *ramMethod = std::get<1>(recv); + auto &methodInfo = std::get<2>(recv); + + if (comp && comp->getOption(TR_UseSymbolValidationManager)) + { + if (!comp->getSymbolValidationManager()->addInterfaceMethodFromCPRecord((TR_OpaqueMethodBlock *) ramMethod, + (TR_OpaqueClassBlock *) ((TR_J9VM *) _fe)->getClassFromMethodBlock(getPersistentIdentifier()), + classObject, + cpIndex)) + { + return NULL; + } + } + + + // If the method ref is unresolved, the bytecodes of the ramMethod will be NULL. + // IFF resolved, then we can look at the rest of the ref. + // + if (resolved) + { + TR_AOTInliningStats *aotStats = NULL; + if (comp->getOption(TR_EnableAOTStats)) + aotStats = & (((TR_JitPrivateConfig *)_fe->_jitConfig->privateConfig)->aotStats->interfaceMethods); + TR_ResolvedMethod *m = createResolvedMethodFromJ9Method(comp, cpIndex, 0, ramMethod, NULL, aotStats, methodInfo); + + TR_OpaqueClassBlock *c = NULL; + if (m) + { + c = m->classOfMethod(); + if (c && !_fe->isInterfaceClass(c)) + { + TR::DebugCounter::incStaticDebugCounter(comp, "resources.resolvedMethods/interface"); + TR::DebugCounter::incStaticDebugCounter(comp, "resources.resolvedMethods/interface:#bytes", sizeof(TR_ResolvedJ9Method)); + resolvedMethod = m; + } + } + } + // for resolved interface method, need to know cpIndex, as well as classObject + // to uniquely identify it. + if (resolvedMethod) + { + compInfoPT->cacheResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::Interface, clazz, cpIndex, classObject), (TR_OpaqueMethodBlock *) ramMethod, 0, methodInfo); + return resolvedMethod; + } + + TR::DebugCounter::incStaticDebugCounter(comp, "resources.resolvedMethods/interface/null"); + return 0; + } + +U_32 +TR_ResolvedJ9JITServerMethod::getResolvedInterfaceMethodOffset(TR_OpaqueClassBlock * classObject, I_32 cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedInterfaceMethodOffset, _remoteMirror, classObject, cpIndex); + auto recv = _stream->read(); + return std::get<0>(recv); + } + +/* Only returns non-null if the method is not to be dispatched by itable, i.e. + * if it is: + * - private (isPrivate()), using direct dispatch; + * - a final method of Object (isFinalInObject()), using direct dispatch; or + * - a non-final method of Object, using virtual dispatch. + */ +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedImproperInterfaceMethod(TR::Compilation * comp, I_32 cpIndex) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); +#if TURN_OFF_INLINING + return 0; +#else + if (!(_fe->_compInfoPT->getClientData()->getRtResolve())) + { + // check for cache first + auto compInfoPT = (TR::CompilationInfoPerThreadRemote *) _fe->_compInfoPT; + TR_ResolvedMethod * resolvedMethod = NULL; + if (compInfoPT->getCachedResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::ImproperInterface, (TR_OpaqueClassBlock *) _ramClass, cpIndex), this, &resolvedMethod)) + return resolvedMethod; + + // query for resolved method and create its mirror at the same time + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedImproperInterfaceMethodAndMirror, _remoteMirror, cpIndex); + auto recv = _stream->read(); + auto j9method = std::get<0>(recv); + auto methodInfo = std::get<1>(recv); + + if (comp->getOption(TR_UseSymbolValidationManager) && j9method) + { + if (!comp->getSymbolValidationManager()->addImproperInterfaceMethodFromCPRecord((TR_OpaqueMethodBlock *)j9method, cp(), cpIndex)) + j9method = NULL; + } + + compInfoPT->cacheResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::ImproperInterface, (TR_OpaqueClassBlock *) _ramClass, cpIndex), (TR_OpaqueMethodBlock *) j9method, 0, methodInfo); + if (j9method == NULL) + return NULL; + else + return createResolvedMethodFromJ9Method(comp, cpIndex, 0, j9method, NULL, NULL, methodInfo); + } + + return NULL; +#endif + } + +void * +TR_ResolvedJ9JITServerMethod::startAddressForJNIMethod(TR::Compilation *comp) + { + // For fastJNI methods, we have the address cached + if (_jniProperties) + return _jniTargetAddress; + _stream->write(JITServer::MessageType::ResolvedMethod_startAddressForJNIMethod, _remoteMirror); + return std::get<0>(_stream->read()); + } + +void * +TR_ResolvedJ9JITServerMethod::startAddressForInterpreterOfJittedMethod() + { + _stream->write(JITServer::MessageType::ResolvedMethod_startAddressForInterpreterOfJittedMethod, _remoteMirror); + return std::get<0>(_stream->read()); + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedVirtualMethod(TR::Compilation * comp, TR_OpaqueClassBlock * classObject, I_32 virtualCallOffset, bool ignoreRtResolve) + { + TR_J9VMBase *fej9 = (TR_J9VMBase *)_fe; + if (_fe->_compInfoPT->getClientData()->getRtResolve() && !ignoreRtResolve) + return NULL; + + auto compInfoPT = (TR::CompilationInfoPerThreadRemote *) _fe->_compInfoPT; + TR_OpaqueClassBlock *clazz = (TR_OpaqueClassBlock *) _ramClass; + TR_ResolvedMethod *resolvedMethod = NULL; + // If method is already cached, return right away + if (compInfoPT->getCachedResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::VirtualFromOffset, clazz, virtualCallOffset, classObject), this, &resolvedMethod)) + return resolvedMethod; + + // Remote call finds RAM method at offset and creates a resolved method at the client + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedVirtualMethod, classObject, virtualCallOffset, ignoreRtResolve, (TR_ResolvedJ9Method *) getRemoteMirror()); + auto recv = _stream->read(); + auto ramMethod = std::get<0>(recv); + auto &methodInfo = std::get<1>(recv); + + // it seems better to override this method for AOT but that's what baseline is doing + // maybe there is a reason for it + if (_fe->isAOT_DEPRECATED_DO_NOT_USE()) + { + if (comp && comp->getOption(TR_UseSymbolValidationManager)) + { + if (!comp->getSymbolValidationManager()->addVirtualMethodFromOffsetRecord(ramMethod, classObject, virtualCallOffset, ignoreRtResolve)) + return NULL; + } + resolvedMethod = ramMethod ? new (comp->trHeapMemory()) TR_ResolvedRelocatableJ9JITServerMethod((TR_OpaqueMethodBlock *) ramMethod, _fe, comp->trMemory(), methodInfo, this) : 0; + } + else + { + resolvedMethod = ramMethod ? new (comp->trHeapMemory()) TR_ResolvedJ9JITServerMethod((TR_OpaqueMethodBlock *) ramMethod, _fe, comp->trMemory(), methodInfo, this) : 0; + } + if (resolvedMethod) + compInfoPT->cacheResolvedMethod(compInfoPT->getResolvedMethodKey(TR_ResolvedMethodType::VirtualFromOffset, clazz, virtualCallOffset, classObject), ramMethod, 0, methodInfo); + return resolvedMethod; + } + +bool +TR_ResolvedJ9JITServerMethod::inROMClass(void * address) + { + return address >= romClassPtr() && address < ((uint8_t*)romClassPtr()) + romClassPtr()->romSize; + } + +char * +TR_ResolvedJ9JITServerMethod::getRemoteROMString(int32_t &len, void *basePtr, std::initializer_list offsets) + { + auto offsetFirst = *offsets.begin(); + auto offsetSecond = (offsets.size() == 2) ? *(offsets.begin() + 1) : 0; + + TR_ASSERT(offsetFirst < (1 << 16) && offsetSecond < (1 << 16), "Offsets are larger than 16 bits"); + TR_ASSERT(offsets.size() <= 2, "Number of offsets is greater than 2"); + + // create a key for hashing into a table of strings + TR_RemoteROMStringKey key; + uint32_t offsetKey = (offsetFirst << 16) + offsetSecond; + key._basePtr = basePtr; + key._offsets = offsetKey; + + std::string *cachedStr = NULL; + bool isCached = false; + TR::CompilationInfoPerThread *threadCompInfo = _fe->_compInfoPT; + { + OMR::CriticalSection getRemoteROMClass(threadCompInfo->getClientData()->getROMMapMonitor()); + auto &stringsCache = getJ9ClassInfo(threadCompInfo, _ramClass)._remoteROMStringsCache; + auto gotStr = stringsCache.find(key); + if (gotStr != stringsCache.end()) + { + cachedStr = &(gotStr->second); + isCached = true; + } + } + + // only make a query if a string hasn't been cached + if (!isCached) + { + size_t offsetFromROMClass = (uint8_t*) basePtr - (uint8_t*) romClassPtr(); + std::string offsetsStr((char*) offsets.begin(), offsets.size() * sizeof(size_t)); + + _stream->write(JITServer::MessageType::ResolvedMethod_getRemoteROMString, _remoteMirror, offsetFromROMClass, offsetsStr); + { + // reaquire the monitor + OMR::CriticalSection getRemoteROMClass(threadCompInfo->getClientData()->getROMMapMonitor()); + auto &stringsCache = getJ9ClassInfo(threadCompInfo, _ramClass)._remoteROMStringsCache; + cachedStr = &(stringsCache.insert({key, std::get<0>(_stream->read())}).first->second); + } + } + + len = cachedStr->length(); + return &(cachedStr->at(0)); + } + +// Takes a pointer to some data which is placed statically relative to the rom class, +// as well as a list of offsets to J9SRP fields. The first offset is applied before the first +// SRP is followed. +// +// If at any point while following the chain of SRP pointers we land outside the ROM class, +// then we fall back to getRemoteROMString which follows the same process on the client. +// +// This is a workaround because some data referenced in the ROM constant pool is located outside of +// it, but we cannot easily determine if the reference is outside or not (or even if it is a reference!) +// because the data is untyped. +char * +TR_ResolvedJ9JITServerMethod::getROMString(int32_t &len, void *basePtr, std::initializer_list offsets) + { + uint8_t *ptr = (uint8_t*) basePtr; + for (size_t offset : offsets) + { + ptr += offset; + if (!inROMClass(ptr)) + return getRemoteROMString(len, basePtr, offsets); + ptr = ptr + *(J9SRP*)ptr; + } + if (!inROMClass(ptr)) + return getRemoteROMString(len, basePtr, offsets); + char *data = utf8Data((J9UTF8*) ptr, len); + return data; + } + +char * +TR_ResolvedJ9JITServerMethod::fieldOrStaticSignatureChars(I_32 cpIndex, int32_t & len) + { + if (cpIndex < 0) + return 0; + + char *signature = getROMString(len, &romCPBase()[cpIndex], + { + offsetof(J9ROMFieldRef, nameAndSignature), + offsetof(J9ROMNameAndSignature, signature) + }); + return signature; + } + +char * +TR_ResolvedJ9JITServerMethod::getClassNameFromConstantPool(uint32_t cpIndex, uint32_t &length) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); + + int32_t len; + char *name = getROMString(len, &romLiterals()[cpIndex], + { + offsetof(J9ROMClassRef, name), + }); + length = len; // this is what happends when you have no type conventions + return name; + } + +char * +TR_ResolvedJ9JITServerMethod::classNameOfFieldOrStatic(I_32 cpIndex, int32_t & len) + { + if (cpIndex == -1) + return 0; + + J9ROMFieldRef * ref = (J9ROMFieldRef *) (&romCPBase()[cpIndex]); + TR_ASSERT(inROMClass(ref), "field ref must be in ROM class"); // for now. if this fails make it a remote call + char *name = getROMString(len, &romCPBase()[ref->classRefCPIndex], + { + offsetof(J9ROMClassRef, name), + }); + return name; + } + +char * +TR_ResolvedJ9JITServerMethod::classSignatureOfFieldOrStatic(I_32 cpIndex, int32_t & len) + { + if (cpIndex == -1) + return 0; + + char *signature = getROMString(len, &romCPBase()[cpIndex], + { + offsetof(J9ROMFieldRef, nameAndSignature), + offsetof(J9ROMNameAndSignature, signature), + }); + return signature; + } + +char * +TR_ResolvedJ9JITServerMethod::fieldOrStaticNameChars(I_32 cpIndex, int32_t & len) + { + if (cpIndex < 0) + return 0; + + char *name = getROMString(len, &romCPBase()[cpIndex], + { + offsetof(J9ROMFieldRef, nameAndSignature), + offsetof(J9ROMNameAndSignature, name) + }); + return name; + } + +char * +TR_ResolvedJ9JITServerMethod::fieldOrStaticName(I_32 cpIndex, int32_t & len, TR_Memory * trMemory, TR_AllocationKind kind) + { + if (cpIndex == -1) + return ""; + + J9ROMFieldRef * ref = (J9ROMFieldRef *) (&romCPBase()[cpIndex]); + J9ROMNameAndSignature * nameAndSignature = J9ROMFIELDREF_NAMEANDSIGNATURE(ref); + + if (inROMClass(nameAndSignature)) + { + J9UTF8 * declName = J9ROMCLASSREF_NAME((J9ROMClassRef *) (&romCPBase()[ref->classRefCPIndex])); + J9UTF8 * name = J9ROMNAMEANDSIGNATURE_NAME(nameAndSignature); + J9UTF8 * signature = J9ROMNAMEANDSIGNATURE_SIGNATURE(nameAndSignature); + + if (inROMClass(declName) && inROMClass(name) && inROMClass(signature)) + { + len = J9UTF8_LENGTH(declName) + J9UTF8_LENGTH(name) + J9UTF8_LENGTH(signature) +3; + char * s = (char *)trMemory->allocateMemory(len, kind); + sprintf(s, "%.*s.%.*s %.*s", + J9UTF8_LENGTH(declName), utf8Data(declName), + J9UTF8_LENGTH(name), utf8Data(name), + J9UTF8_LENGTH(signature), utf8Data(signature)); + return s; + } + } + + + std::string *cachedStr = NULL; + bool isCached = false; + TR::CompilationInfoPerThread *threadCompInfo = _fe->_compInfoPT; + { + OMR::CriticalSection getRemoteROMClass(threadCompInfo->getClientData()->getROMMapMonitor()); + auto &stringsCache = getJ9ClassInfo(threadCompInfo, _ramClass)._fieldOrStaticNameCache; + auto gotStr = stringsCache.find(cpIndex); + if (gotStr != stringsCache.end()) + { + cachedStr = &(gotStr->second); + isCached = true; + } + } + + // only make a query if a string hasn't been cached + if (!isCached) + { + _stream->write(JITServer::MessageType::ResolvedMethod_fieldOrStaticName, _remoteMirror, cpIndex); + { + // reaquire the monitor + OMR::CriticalSection getRemoteROMClass(threadCompInfo->getClientData()->getROMMapMonitor()); + auto &stringsCache = getJ9ClassInfo(threadCompInfo, _ramClass)._fieldOrStaticNameCache; + cachedStr = &(stringsCache.insert({cpIndex, std::get<0>(_stream->read())}).first->second); + } + } + + len = cachedStr->length(); + return &(cachedStr->at(0)); + } + +void * +TR_ResolvedJ9JITServerMethod::stringConstant(I_32 cpIndex) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); + _stream->write(JITServer::MessageType::ResolvedMethod_stringConstant, _remoteMirror, cpIndex); + auto recv = _stream->read(); + + auto compInfoPT = static_cast(_fe->_compInfoPT); + compInfoPT->cacheIsUnresolvedStr((TR_OpaqueClassBlock *) _ramClass, cpIndex, TR_IsUnresolvedString(std::get<1>(recv), std::get<2>(recv))); + return std::get<0>(recv); + } + +bool +TR_ResolvedJ9JITServerMethod::isUnresolvedString(I_32 cpIndex, bool optimizeForAOT) + { + auto compInfoPT = static_cast(_fe->_compInfoPT); + TR_IsUnresolvedString stringAttrs; + if (compInfoPT->getCachedIsUnresolvedStr((TR_OpaqueClassBlock *) _ramClass, cpIndex, stringAttrs)) + { + return optimizeForAOT ? stringAttrs._optimizeForAOTTrueResult : stringAttrs._optimizeForAOTFalseResult; + } + else + { + _stream->write(JITServer::MessageType::ResolvedMethod_isUnresolvedString, _remoteMirror, cpIndex, optimizeForAOT); + return std::get<0>(_stream->read()); + } + } + +bool +TR_ResolvedJ9JITServerMethod::isSubjectToPhaseChange(TR::Compilation *comp) + { + bool candidate = comp->getOptLevel() <= warm && + // comp->getPersistentInfo()->getJitState() == STARTUP_STATE // This needs to be asked at the server + isPublic() && + ( + strncmp("java/util/AbstractCollection", comp->signature(), 28) == 0 || + strncmp("java/util/Hash", comp->signature(), 14) == 0 || + strncmp("java/lang/String", comp->signature(), 16) == 0 || + strncmp("sun/nio/", comp->signature(), 8) == 0 + ); + + if (!candidate) + { + return false; + } + else + { + _stream->write(JITServer::MessageType::ResolvedMethod_isSubjectToPhaseChange, _remoteMirror); + return std::get<0>(_stream->read()); + // JITServer TODO: cache the JitState when we create TR_ResolvedJ9JITServerMethod + // This may not behave exactly like the non-JITServer due to timing differences + } + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedHandleMethod(TR::Compilation * comp, I_32 cpIndex, bool * unresolvedInCP) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); +#if TURN_OFF_INLINING + return 0; +#else + + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedHandleMethod, _remoteMirror, cpIndex); + auto recv = _stream->read(); + auto dummyInvokeExact = std::get<0>(recv); + auto signature = std::get<1>(recv); + if (unresolvedInCP) + *unresolvedInCP = std::get<2>(recv); + + return _fe->createResolvedMethodWithSignature(comp->trMemory(), dummyInvokeExact, NULL, &signature[0], signature.length(), this); +#endif + } + +#if defined(J9VM_OPT_REMOVE_CONSTANT_POOL_SPLITTING) +void * +TR_ResolvedJ9JITServerMethod::methodTypeTableEntryAddress(int32_t cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_methodTypeTableEntryAddress, _remoteMirror, cpIndex); + return std::get<0>(_stream->read()); + } + +bool +TR_ResolvedJ9JITServerMethod::isUnresolvedMethodTypeTableEntry(int32_t cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_isUnresolvedMethodTypeTableEntry, _remoteMirror, cpIndex); + return std::get<0>(_stream->read()); + } +#endif + +bool +TR_ResolvedJ9JITServerMethod::isUnresolvedCallSiteTableEntry(int32_t callSiteIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_isUnresolvedCallSiteTableEntry, _remoteMirror, callSiteIndex); + return std::get<0>(_stream->read()); + } + +void * +TR_ResolvedJ9JITServerMethod::callSiteTableEntryAddress(int32_t callSiteIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_callSiteTableEntryAddress, _remoteMirror, callSiteIndex); + return std::get<0>(_stream->read()); + } + +bool +TR_ResolvedJ9JITServerMethod::isUnresolvedVarHandleMethodTypeTableEntry(int32_t cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_isUnresolvedVarHandleMethodTypeTableEntry, _remoteMirror, cpIndex); + return std::get<0>(_stream->read()); + } + +void * +TR_ResolvedJ9JITServerMethod::varHandleMethodTypeTableEntryAddress(int32_t cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_varHandleMethodTypeTableEntryAddress, _remoteMirror, cpIndex); + return std::get<0>(_stream->read()); + } + +TR_ResolvedMethod * +TR_ResolvedJ9JITServerMethod::getResolvedDynamicMethod(TR::Compilation * comp, I_32 callSiteIndex, bool * unresolvedInCP) + { + TR_ASSERT(callSiteIndex != -1, "callSiteIndex shouldn't be -1"); + +#if TURN_OFF_INLINING + return 0; +#else + + J9Class *ramClass = constantPoolHdr(); + J9ROMClass *romClass = romClassPtr(); + _stream->write(JITServer::MessageType::ResolvedMethod_getResolvedDynamicMethod, callSiteIndex, ramClass); + auto recv = _stream->read(); + TR_OpaqueMethodBlock *dummyInvokeExact = std::get<0>(recv); + std::string signature = std::get<1>(recv); + if (unresolvedInCP) + *unresolvedInCP = std::get<2>(recv); + TR_ResolvedMethod *result = _fe->createResolvedMethodWithSignature(comp->trMemory(), dummyInvokeExact, NULL, &signature[0], signature.size(), this); + + return result; +#endif + } + +bool +TR_ResolvedJ9JITServerMethod::shouldFailSetRecognizedMethodInfoBecauseOfHCR() + { + _stream->write(JITServer::MessageType::ResolvedMethod_shouldFailSetRecognizedMethodInfoBecauseOfHCR, _remoteMirror); + return std::get<0>(_stream->read()); + } + +bool +TR_ResolvedJ9JITServerMethod::isSameMethod(TR_ResolvedMethod * m2) + { + if (isNative()) + return false; // A jitted JNI method doesn't call itself + + auto other = static_cast(m2); + + bool sameRamMethod = ramMethod() == other->ramMethod(); + if (!sameRamMethod) + return false; + + if (asJ9Method()->isArchetypeSpecimen()) + { + if (!other->asJ9Method()->isArchetypeSpecimen()) + return false; + + uintptrj_t *thisHandleLocation = getMethodHandleLocation(); + uintptrj_t *otherHandleLocation = other->getMethodHandleLocation(); + + // If these are not MethodHandle thunk archetypes, then we're not sure + // how to compare them. Conservatively return false in that case. + // + if (!thisHandleLocation) + return false; + if (!otherHandleLocation) + return false; + + bool sameMethodHandle; + + _stream->write(JITServer::MessageType::ResolvedMethod_isSameMethod, thisHandleLocation, otherHandleLocation); + sameMethodHandle = std::get<0>(_stream->read()); + + if (sameMethodHandle) + { + // Same ramMethod, same handle. This means we're talking about the + // exact same thunk. + // + return true; + } + else + { + // Different method handle. Assuming we're talking about a custom thunk, + // then it will be different thunk. + // + return false; + } + } + + return true; + } + +bool +TR_ResolvedJ9JITServerMethod::isInlineable(TR::Compilation *comp) + { + // Reduce number of remote queries by testing the options first + // This assumes knowledge of how the original is implemented + if (comp->getOption(TR_FullSpeedDebug) && comp->getOption(TR_EnableOSR)) + { + _stream->write(JITServer::MessageType::ResolvedMethod_isInlineable, _remoteMirror); + return std::get<0>(_stream->read()); + } + else + { + return true; + } + } + +void +TR_ResolvedJ9JITServerMethod::setWarmCallGraphTooBig(uint32_t bcIndex, TR::Compilation *comp) + { + // set the variable in the server IProfiler + TR_ResolvedJ9Method::setWarmCallGraphTooBig(bcIndex, comp); + // set it on the client IProfiler too, in case a server crashes, client will still have the correct value + _stream->write(JITServer::MessageType::ResolvedMethod_setWarmCallGraphTooBig, _remoteMirror, bcIndex); + _stream->read(); + } + +void +TR_ResolvedJ9JITServerMethod::setVirtualMethodIsOverridden() + { + _stream->write(JITServer::MessageType::ResolvedMethod_setVirtualMethodIsOverridden, _remoteMirror); + _stream->read(); + } + +bool +TR_ResolvedJ9JITServerMethod::methodIsNotzAAPEligible() + { + _stream->write(JITServer::MessageType::ResolvedMethod_methodIsNotzAAPEligible, _remoteMirror); + return std::get<0>(_stream->read()); + } +void +TR_ResolvedJ9JITServerMethod::setClassForNewInstance(J9Class *c) + { + _j9classForNewInstance = c; + _stream->write(JITServer::MessageType::ResolvedMethod_setClassForNewInstance, _remoteMirror, c); + _stream->read(); + } + +TR_OpaqueClassBlock * +TR_ResolvedJ9JITServerMethod::classOfMethod() + { + if (isNewInstanceImplThunk()) + { + TR_ASSERT(_j9classForNewInstance, "Must have the class for the newInstance"); + //J9Class * clazz = (J9Class *)((intptrj_t)ramMethod()->extra & ~J9_STARTPC_NOT_TRANSLATED); + return _fe->convertClassPtrToClassOffset(_j9classForNewInstance);//(TR_OpaqueClassBlock *&)(rc); + } + return _fe->convertClassPtrToClassOffset(_ramClass); + } + +TR_PersistentJittedBodyInfo * +TR_ResolvedJ9JITServerMethod::getExistingJittedBodyInfo() + { + return _bodyInfo; // return cached value + } + +void +TR_ResolvedJ9JITServerMethod::getFaninInfo(uint32_t *count, uint32_t *weight, uint32_t *otherBucketWeight) + { + uint32_t i = 0; + uint32_t w = 0; + if (otherBucketWeight) + *otherBucketWeight = 0; + + TR_IPMethodHashTableEntry *entry = _iProfilerMethodEntry; + if (entry) + { + w = entry->_otherBucket.getWeight(); + // Iterate through all the callers and add their weight + for (TR_IPMethodData* it = &entry->_caller; it; it = it->next) + { + w += it->getWeight(); + i++; + } + if (otherBucketWeight) + *otherBucketWeight = entry->_otherBucket.getWeight(); + } + *weight = w; + *count = i; + } + +bool +TR_ResolvedJ9JITServerMethod::getCallerWeight(TR_ResolvedJ9Method *caller, uint32_t *weight, uint32_t pcIndex) + { + TR_OpaqueMethodBlock *callerMethod = caller->getPersistentIdentifier(); + bool useTuples = (pcIndex != ~0); + + //adjust pcIndex for interface calls (see getSearchPCFromMethodAndBCIndex) + //otherwise we won't be able to locate a caller-callee-bcIndex triplet + //even if it is in a TR_IPMethodHashTableEntry + TR_IProfiler *iProfiler = _fe->getIProfiler(); + if (!iProfiler) + return false; + + uintptrj_t pcAddress = iProfiler->getSearchPCFromMethodAndBCIndex(callerMethod, pcIndex, 0); + + TR_IPMethodHashTableEntry *entry = _iProfilerMethodEntry; + + if(!entry) // if there are no entries, we have no callers! + { + *weight = ~0; + return false; + } + for (TR_IPMethodData* it = &entry->_caller; it; it = it->next) + { + if( it->getMethod() == callerMethod && (!useTuples || ((((uintptrj_t) it->getPCIndex()) + TR::Compiler->mtd.bytecodeStart(callerMethod)) == pcAddress))) + { + *weight = it->getWeight(); + return true; + } + } + + *weight = entry->_otherBucket.getWeight(); + return false; + } + +void +TR_ResolvedJ9JITServerMethod::createResolvedMethodMirror(TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_OpaqueMethodBlock *method, uint32_t vTableSlot, TR_ResolvedMethod *owningMethod, TR_FrontEnd *fe, TR_Memory *trMemory) + { + // Create resolved method mirror on the client. + // Should be called to mirror a call to resolved method constructor, + // will work when method doesn't have an owning method (compilee method). + // Resolved method should not be NULL + TR_ResolvedJ9Method *resolvedMethod = NULL; + if (!((TR_J9VMBase *) fe)->isAOT_DEPRECATED_DO_NOT_USE()) + resolvedMethod = new (trMemory->trHeapMemory()) TR_ResolvedJ9Method(method, fe, trMemory, owningMethod, vTableSlot); + else + resolvedMethod = new (trMemory->trHeapMemory()) TR_ResolvedRelocatableJ9Method(method, fe, trMemory, owningMethod, vTableSlot); + if (!resolvedMethod) throw std::bad_alloc(); + + packMethodInfo(methodInfo, resolvedMethod, fe); + } + +void +TR_ResolvedJ9JITServerMethod::createResolvedMethodFromJ9MethodMirror(TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_OpaqueMethodBlock *method, uint32_t vTableSlot, TR_ResolvedMethod *owningMethod, TR_FrontEnd *fe, TR_Memory *trMemory) + { + // Create resolved method mirror on the client. + // Should be called to mirror a call to TR_Resolved(Relocatable)MethodFromJ9Method::createResolvedMethodFromJ9Method. + // Requires owning method to exist to work properly. + // Resolved method can be NULL. + TR_ASSERT(owningMethod, "owning method cannot be NULL"); + TR_ResolvedJ9Method *resolvedMethod = NULL; + // The simplest solution would be to call owningMethod->createResolvedMethodFromJ9Method(...) here. + // Thanks to polymorphism, the correct version of the resolved method would be initialized and everything would be simple. + // However, createResolvedMethodFromJ9Method is declared protected in TR_ResolvedJ9Method, so we can't call it here. + // Maybe we should make it public, but that would require changing baseline. + // For now, I'll have to do with mostly copy-pasting code from that method. + TR_J9VMBase *fej9 = (TR_J9VMBase *) fe; + if (!fej9->isAOT_DEPRECATED_DO_NOT_USE()) + { + resolvedMethod = new (trMemory->trHeapMemory()) TR_ResolvedJ9Method(method, fe, trMemory, owningMethod, vTableSlot); + if (!resolvedMethod) throw std::bad_alloc(); + } + else + { + // Do the same thing createResolvedMethodFromJ9Method does, but without collecting stats + TR::Compilation *comp = TR::comp(); +#if defined(J9VM_OPT_SHARED_CLASSES) && (defined(TR_HOST_X86) || defined(TR_HOST_POWER) || defined(TR_HOST_S390) || defined(TR_HOST_ARM)) + bool resolveAOTMethods = !comp->getOption(TR_DisableAOTResolveDiffCLMethods); + bool enableAggressive = comp->getOption(TR_EnableAOTInlineSystemMethod); + bool isSystemClassLoader = false; + J9Method *j9method = (J9Method *) method; + + if (comp->getOption(TR_DisableDFP) || + (!(TR::Compiler->target.cpu.supportsDecimalFloatingPoint() +#ifdef TR_TARGET_S390 + || TR::Compiler->target.cpu.getSupportsDecimalFloatingPointFacility() +#endif + ) || + !TR_J9MethodBase::isBigDecimalMethod(j9method))) + { + // Check if same classloader + J9Class *j9clazz = (J9Class *) J9_CLASS_FROM_CP(((J9RAMConstantPoolItem *) J9_CP_FROM_METHOD(((J9Method *)j9method)))); + TR_OpaqueClassBlock *clazzOfInlinedMethod = fej9->convertClassPtrToClassOffset(j9clazz); + TR_OpaqueClassBlock *clazzOfCompiledMethod = fej9->convertClassPtrToClassOffset(J9_CLASS_FROM_METHOD(((TR_ResolvedJ9Method *) owningMethod)->ramMethod())); + + if (enableAggressive) + { + isSystemClassLoader = ((void*)fej9->vmThread()->javaVM->systemClassLoader->classLoaderObject == (void*)fej9->getClassLoader(clazzOfInlinedMethod)); + } + + if (fej9->sharedCache()->isPointerInSharedCache(J9_CLASS_FROM_METHOD(j9method)->romClass)) + { + bool sameLoaders = false; + TR_J9VMBase *fej9 = (TR_J9VMBase *)fe; + if (resolveAOTMethods || + (sameLoaders = fej9->sameClassLoaders(clazzOfInlinedMethod, clazzOfCompiledMethod)) || + isSystemClassLoader) + { + resolvedMethod = new (comp->trHeapMemory()) TR_ResolvedRelocatableJ9Method((TR_OpaqueMethodBlock *) j9method, fe, comp->trMemory(), owningMethod, vTableSlot); + if (!resolvedMethod) throw std::bad_alloc(); + } + } + } + +#endif + } + + packMethodInfo(methodInfo, resolvedMethod, fe); + } + +void +TR_ResolvedJ9JITServerMethod::packMethodInfo(TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_ResolvedJ9Method *resolvedMethod, TR_FrontEnd *fe) + { + auto &methodInfoStruct = std::get<0>(methodInfo); + if (!resolvedMethod) + { + // resolved method not created, setting remoteMirror to NULL indicates + // that other fields should be ignored. + methodInfoStruct.remoteMirror = NULL; + return; + } + + + TR::Compilation *comp = TR::comp(); + + // retrieve all relevant attributes of resolvedMethod and set them in methodInfo + J9RAMConstantPoolItem *literals = (J9RAMConstantPoolItem *)(J9_CP_FROM_METHOD(resolvedMethod->ramMethod())); + J9Class *cpHdr = J9_CLASS_FROM_CP(literals); + + J9Method *j9method = resolvedMethod->ramMethod(); + + // fill-in struct fields + methodInfoStruct.remoteMirror = resolvedMethod; + methodInfoStruct.literals = literals; + methodInfoStruct.ramClass = cpHdr; + methodInfoStruct.methodIndex = getMethodIndexUnchecked(j9method); + methodInfoStruct.jniProperties = resolvedMethod->getJNIProperties(); + methodInfoStruct.jniTargetAddress = resolvedMethod->getJNITargetAddress(); + methodInfoStruct.isInterpreted = resolvedMethod->isInterpreted(); + methodInfoStruct.isJNINative = resolvedMethod->isJNINative(); + methodInfoStruct.isMethodInValidLibrary = resolvedMethod->isMethodInValidLibrary(); + methodInfoStruct.mandatoryRm = resolvedMethod->getMandatoryRecognizedMethod(); + methodInfoStruct.rm = ((TR_ResolvedMethod*)resolvedMethod)->getRecognizedMethod(); + methodInfoStruct.startAddressForJittedMethod = TR::CompilationInfo::isCompiled(resolvedMethod->ramMethod()) ? + resolvedMethod->startAddressForJittedMethod() : NULL; + methodInfoStruct.virtualMethodIsOverridden = resolvedMethod->virtualMethodIsOverridden(); + methodInfoStruct.addressContainingIsOverriddenBit = resolvedMethod->addressContainingIsOverriddenBit(); + methodInfoStruct.classLoader = resolvedMethod->getClassLoader(); + + TR_PersistentJittedBodyInfo *bodyInfo = NULL; + // Method may not have been compiled + if (!resolvedMethod->isInterpreted() && !resolvedMethod->isJITInternalNative()) + { + bodyInfo = resolvedMethod->getExistingJittedBodyInfo(); + } + + // set string info fields (they cannot be inside of the struct) + std::string jbi = bodyInfo ? std::string((char*)bodyInfo, sizeof(TR_PersistentJittedBodyInfo)) : std::string(); + std::string methodInfoStr = bodyInfo ? std::string((char*)bodyInfo->getMethodInfo(), sizeof(TR_PersistentMethodInfo)) : std::string(); + std::get<1>(methodInfo) = jbi; + std::get<2>(methodInfo) = methodInfoStr; + + // set IP method data string. + // fanin info is not used at cold opt level, so there is no point sending this information to the server + JITClientIProfiler *iProfiler = (JITClientIProfiler *)((TR_J9VMBase *) fe)->getIProfiler(); + std::get<3>(methodInfo) = (comp && comp->getOptLevel() >= warm && iProfiler) ? iProfiler->serializeIProfilerMethodEntry(resolvedMethod->getPersistentIdentifier()) : std::string(); + } + +void +TR_ResolvedJ9JITServerMethod::unpackMethodInfo(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd * fe, TR_Memory * trMemory, uint32_t vTableSlot, TR::CompilationInfoPerThread *threadCompInfo, const TR_ResolvedJ9JITServerMethodInfo &methodInfo) + { + auto methodInfoStruct = std::get<0>(methodInfo); + + + _ramMethod = (J9Method *)aMethod; + + _remoteMirror = methodInfoStruct.remoteMirror; + + // Cache the constantPool and constantPoolHeader + _literals = methodInfoStruct.literals; + _ramClass = methodInfoStruct.ramClass; + + _romClass = threadCompInfo->getAndCacheRemoteROMClass(_ramClass, trMemory); + _romMethod = romMethodAtClassIndex(_romClass, methodInfoStruct.methodIndex); + _romLiterals = (J9ROMConstantPoolItem *) ((UDATA) _romClass + sizeof(J9ROMClass)); + + _vTableSlot = vTableSlot; + _j9classForNewInstance = NULL; + + _jniProperties = methodInfoStruct.jniProperties; + _jniTargetAddress = methodInfoStruct.jniTargetAddress; + + _isInterpreted = methodInfoStruct.isInterpreted; + _isJNINative = methodInfoStruct.isJNINative; + _isMethodInValidLibrary = methodInfoStruct.isMethodInValidLibrary; + + TR::RecognizedMethod mandatoryRm = methodInfoStruct.mandatoryRm; + TR::RecognizedMethod rm = methodInfoStruct.rm; + + _startAddressForJittedMethod = methodInfoStruct.startAddressForJittedMethod; + _virtualMethodIsOverridden = methodInfoStruct.virtualMethodIsOverridden; + _addressContainingIsOverriddenBit = methodInfoStruct.addressContainingIsOverriddenBit; + _classLoader = methodInfoStruct.classLoader; + + auto &bodyInfoStr = std::get<1>(methodInfo); + auto &methodInfoStr = std::get<2>(methodInfo); + + _bodyInfo = J9::Recompilation::persistentJittedBodyInfoFromString(bodyInfoStr, methodInfoStr, trMemory); + + // initialization from TR_J9Method constructor + _className = J9ROMCLASS_CLASSNAME(_romClass); + _name = J9ROMMETHOD_NAME(_romMethod); + _signature = J9ROMMETHOD_SIGNATURE(_romMethod); + parseSignature(trMemory); + _fullSignature = NULL; + + setMandatoryRecognizedMethod(mandatoryRm); + setRecognizedMethod(rm); + + JITServerIProfiler *iProfiler = (JITServerIProfiler *) ((TR_J9VMBase *) fe)->getIProfiler(); + const std::string entryStr = std::get<3>(methodInfo); + const auto serialEntry = (TR_ContiguousIPMethodHashTableEntry*) &entryStr[0]; + _iProfilerMethodEntry = (iProfiler && !entryStr.empty()) ? iProfiler->deserializeMethodEntry(serialEntry, trMemory) : NULL; + } + +bool +TR_ResolvedJ9JITServerMethod::addValidationRecordForCachedResolvedMethod(const TR_ResolvedMethodKey &key, TR_OpaqueMethodBlock *method) + { + // This method should be called whenever this resolved method is fetched from + // cache during AOT compilation, because + // if the cached resolved method was created while in heuristic region, + // SVM record would not be added. + auto svm = _fe->_compInfoPT->getCompilation()->getSymbolValidationManager(); + int32_t cpIndex = key.cpIndex; + TR_OpaqueClassBlock *classObject = key.classObject; + bool added = false; + J9ConstantPool *cp = (J9ConstantPool *) static_cast(this)->cp(); + switch (key.type) + { + case VirtualFromCP: + added = svm->addVirtualMethodFromCPRecord(method, cp, cpIndex); + break; + case VirtualFromOffset: + added = svm->addVirtualMethodFromOffsetRecord(method, classObject, key.cpIndex, false); + break; + case Interface: + added = svm->addInterfaceMethodFromCPRecord( + method, + (TR_OpaqueClassBlock *) ((TR_J9VM *) _fe)->getClassFromMethodBlock(getPersistentIdentifier()), + classObject, + cpIndex); + break; + case Static: + added = svm->addStaticMethodFromCPRecord(method, cp, cpIndex); + break; + case Special: + added = svm->addSpecialMethodFromCPRecord(method, cp, cpIndex); + break; + case ImproperInterface: + added = svm->addImproperInterfaceMethodFromCPRecord(method, cp, cpIndex); + break; + default: + TR_ASSERT(false, "Invalid TR_ResolvedMethodType value"); + } + return added; + } + +void +TR_ResolvedJ9JITServerMethod::cacheResolvedMethodsCallees() + { + // 1. Iterate through bytecodes and look for method invokes. + // If resolved method corresponding to an invoke is not cached, add it + // to the list of methods that will be sent to the client in one batch. + auto compInfoPT = (TR::CompilationInfoPerThreadRemote *) _fe->_compInfoPT; + TR_J9ByteCodeIterator bci(0, this, fej9(), compInfoPT->getCompilation()); + std::vector cpIndices; + std::vector methodTypes; + for(TR_J9ByteCode bc = bci.first(); bc != J9BCunknown; bc = bci.next()) + { + // Identify all bytecodes that require a resolved method + int32_t cpIndex = bci.next2Bytes(); + TR_ResolvedMethodType type = TR_ResolvedMethodType::NoType; + TR_ResolvedMethod *resolvedMethod; + switch (bc) + { + case J9BCinvokevirtual: + { + type = TR_ResolvedMethodType::VirtualFromCP; + break; + } + case J9BCinvokestaticsplit: + { + // falling through on purpose + cpIndex |= J9_STATIC_SPLIT_TABLE_INDEX_FLAG; + } + case J9BCinvokestatic: + { + type = TR_ResolvedMethodType::Static; + break; + } + case J9BCinvokespecialsplit: + { + // falling through on purpose + cpIndex |= J9_SPECIAL_SPLIT_TABLE_INDEX_FLAG; + } + case J9BCinvokespecial: + { + type = TR_ResolvedMethodType::Special; + break; + } + default: + { + // do nothing + break; + } + } + + if (type != TR_ResolvedMethodType::NoType && + !compInfoPT->getCachedResolvedMethod( + compInfoPT->getResolvedMethodKey(type, (TR_OpaqueClassBlock *) _ramClass, cpIndex), + this, + &resolvedMethod)) + { + methodTypes.push_back(type); + cpIndices.push_back(cpIndex); + } + } + + int32_t numMethods = methodTypes.size(); + // If less than 2 methods, it's cheaper to create + // resolved method normally, because client won't + // have to deal with vectors + if (numMethods < 2) + return; + + // 2. Send a remote query to mirror all uncached resolved methods + _stream->write(JITServer::MessageType::ResolvedMethod_getMultipleResolvedMethods, (TR_ResolvedJ9Method *) _remoteMirror, methodTypes, cpIndices); + auto recv = _stream->read, std::vector, std::vector>(); + + // 3. Cache all received resolved methods + auto ramMethods = std::get<0>(recv); + auto vTableOffsets = std::get<1>(recv); + auto methodInfos = std::get<2>(recv); + TR_ASSERT(numMethods == ramMethods.size(), "Number of received methods does not match the number of requested methods"); + for (int32_t i = 0; i < numMethods; ++i) + { + TR_ResolvedMethodType type = methodTypes[i]; + TR_ResolvedMethod *resolvedMethod; + TR_ResolvedMethodKey key = compInfoPT->getResolvedMethodKey(type, (TR_OpaqueClassBlock *) _ramClass, cpIndices[i]); + if (std::get<0>(methodInfos[i]).remoteMirror && + !compInfoPT->getCachedResolvedMethod( + key, + this, + &resolvedMethod)) + { + compInfoPT->cacheResolvedMethod( + key, + (TR_OpaqueMethodBlock *) ramMethods[i], + vTableOffsets[i], + methodInfos[i]); + } + } + } + +bool +TR_ResolvedJ9JITServerMethod::validateMethodFieldAttributes(const TR_J9MethodFieldAttributes &attributes, bool isStatic, int32_t cpIndex, bool isStore, bool needAOTValidation) + { + // Validation should not be done against attributes cached per-resolved method, + // because unresolved attribute might have become resolved during the current compilation + // so validation would fail, but since it does not affect functional correctness, + // we'll just keep the cached result until the end of the compilation. + if (attributes.isUnresolvedInCP()) + return true; + if (!isStatic) + _stream->write(JITServer::MessageType::ResolvedMethod_fieldAttributes, _remoteMirror, cpIndex, isStore, needAOTValidation); + else + _stream->write(JITServer::MessageType::ResolvedMethod_staticAttributes, _remoteMirror, cpIndex, isStore, needAOTValidation); + auto recv = _stream->read(); + auto clientAttributes = std::get<0>(recv); + bool equal = (attributes == clientAttributes); + return equal; + } + +U_16 +TR_ResolvedJ9JITServerMethod::archetypeArgPlaceholderSlot() + { + TR_ASSERT(isArchetypeSpecimen(), "should not be called for non-ArchetypeSpecimen methods"); + TR_OpaqueMethodBlock * aMethod = getNonPersistentIdentifier(); + J9ROMMethod * romMethod = JITServerHelpers::romMethodOfRamMethod((J9Method *)aMethod); + J9UTF8 * signature = J9ROMMETHOD_SIGNATURE(romMethod); + + U_8 tempArgTypes[256]; + uintptr_t paramElements; + uintptr_t paramSlots; + jitParseSignature(signature, tempArgTypes, ¶mElements, ¶mSlots); + /* + * result should be : paramSlot + 1 -1 = paramSlot + * +1 :thunk archetype are always virtual method and has a receiver + * -1 :the placeholder is a 1-slot type (int) + */ + return paramSlots; + } + +TR_ResolvedRelocatableJ9JITServerMethod::TR_ResolvedRelocatableJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd * fe, TR_Memory * trMemory, TR_ResolvedMethod * owner, uint32_t vTableSlot) + : TR_ResolvedJ9JITServerMethod(aMethod, fe, trMemory, owner, vTableSlot) + { + // NOTE: avoid using this constructor as much as possible. + // Using may result in multiple remote messages (up to 3?) sent to the client + TR_J9VMBase *fej9 = (TR_J9VMBase *)fe; + TR::Compilation *comp = fej9->_compInfoPT->getCompilation(); + if (comp && this->TR_ResolvedMethod::getRecognizedMethod() != TR::unknownMethod) + { + if (fej9->canRememberClass(containingClass())) + { + if (comp->getOption(TR_UseSymbolValidationManager)) + { + TR::SymbolValidationManager *svm = comp->getSymbolValidationManager(); + SVM_ASSERT_ALREADY_VALIDATED(svm, aMethod); + SVM_ASSERT_ALREADY_VALIDATED(svm, containingClass()); + } + else + { + ((TR_ResolvedRelocatableJ9JITServerMethod *) owner)->validateArbitraryClass(comp, (J9Class*)containingClass()); + } + } + } + } + +TR_ResolvedRelocatableJ9JITServerMethod::TR_ResolvedRelocatableJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd * fe, TR_Memory * trMemory, const TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_ResolvedMethod * owner, uint32_t vTableSlot) + : TR_ResolvedJ9JITServerMethod(aMethod, fe, trMemory, methodInfo, owner, vTableSlot) + { + TR_J9VMBase *fej9 = (TR_J9VMBase *)fe; + TR::Compilation *comp = fej9->_compInfoPT->getCompilation(); + if (comp && this->TR_ResolvedMethod::getRecognizedMethod() != TR::unknownMethod) + { + // in the client-side constructor we check if containing class has been remembered, + // and if it wasn't set recognized method to TR::unknownMethod. + // However, this constructor is passed methodInfo, which means that client-side mirror + // has already been created, attempted to remember the containing class and set recognized method + // to appropriate value, then wrote it to methodInfo. So, we don't need that check on the server. + if (comp->getOption(TR_UseSymbolValidationManager)) + { + TR::SymbolValidationManager *svm = comp->getSymbolValidationManager(); + SVM_ASSERT_ALREADY_VALIDATED(svm, aMethod); + SVM_ASSERT_ALREADY_VALIDATED(svm, containingClass()); + } + else + { + // TODO: this will require more remote messages. + // For simplicity, leave it like this for now, once testing can be done, optimize it + ((TR_ResolvedRelocatableJ9JITServerMethod *) owner)->validateArbitraryClass(comp, (J9Class*)containingClass()); + } + } + } + +void * +TR_ResolvedRelocatableJ9JITServerMethod::constantPool() + { + return romLiterals(); + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::isInterpreted() + { + bool alwaysTreatAsInterpreted = true; +#if defined(TR_TARGET_S390) + alwaysTreatAsInterpreted = false; +#elif defined(TR_TARGET_X86) + + /*if isInterpreted should be only overridden for JNI methods. + Otherwise buildDirectCall in X86PrivateLinkage.cpp will generate CALL 0 + for certain jitted methods as startAddressForJittedMethod still returns NULL in AOT + this is not an issue on z as direct calls are dispatched via snippets + + If one of the options to disable JNI is specified + this function reverts back to the old behaviour + */ + TR::Compilation *comp = _fe->_compInfoPT->getCompilation(); + if (isJNINative() && + !comp->getOption(TR_DisableDirectToJNI) && + !comp->getOption(TR_DisableDirectToJNIInline) && + !comp->getOption(TR_DisableDirectToJNI) && + !comp->getOption(TR_DisableDirectToJNIInline) + ) + { + alwaysTreatAsInterpreted = false; + } +#endif + + if (alwaysTreatAsInterpreted) + return true; + + return TR_ResolvedJ9JITServerMethod::isInterpreted(); + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::isInterpretedForHeuristics() + { + return TR_ResolvedJ9JITServerMethod::isInterpreted(); + } + +TR_OpaqueMethodBlock * +TR_ResolvedRelocatableJ9JITServerMethod::getNonPersistentIdentifier() + { + return (TR_OpaqueMethodBlock *)ramMethod(); + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::validateArbitraryClass(TR::Compilation *comp, J9Class *clazz) + { + return storeValidationRecordIfNecessary(comp, cp(), 0, TR_ValidateArbitraryClass, ramMethod(), clazz); + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::storeValidationRecordIfNecessary(TR::Compilation * comp, J9ConstantPool *constantPool, int32_t cpIndex, TR_ExternalRelocationTargetKind reloKind, J9Method *ramMethod, J9Class *definingClass) + { + TR_J9VMBase *fej9 = (TR_J9VMBase *) comp->fe(); + bool storeClassInfo = true; + bool fieldInfoCanBeUsed = false; + TR_AOTStats *aotStats = ((TR_JitPrivateConfig *)fej9->_jitConfig->privateConfig)->aotStats; + bool isStatic = (reloKind == TR_ValidateStaticField); + + UDATA *classChain = NULL; + auto clientData = _fe->_compInfoPT->getClientData(); + PersistentUnorderedMap &classChainCache = clientData->getClassClainDataCache(); + if (definingClass) + { + // if defining class is known, check if we already have a corresponding class chain cached + OMR::CriticalSection classChainDataMapMonitor(clientData->getClassChainDataMapMonitor()); + auto it = classChainCache.find(definingClass); + if (it != classChainCache.end()) + classChain = it->second; + } + + if (!classChain) + { + _stream->write(JITServer::MessageType::ResolvedRelocatableMethod_storeValidationRecordIfNecessary, ramMethod, constantPool, cpIndex, isStatic, definingClass); + // 1. RAM class of ramMethod + // 2. defining class + // 3. class chain + auto recv = _stream->read(); + + J9Class *clazz = std::get<0>(recv); + traceMsg(comp, "storeValidationRecordIfNecessary:\n"); + traceMsg(comp, "\tconstantPool %p cpIndex %d\n", constantPool, cpIndex); + traceMsg(comp, "\treloKind %d isStatic %d\n", reloKind, isStatic); + J9UTF8 *methodClassName = J9ROMCLASS_CLASSNAME(TR::Compiler->cls.romClassOf((TR_OpaqueClassBlock *) clazz)); + traceMsg(comp, "\tmethod %p from class %p %.*s\n", ramMethod, clazz, J9UTF8_LENGTH(methodClassName), J9UTF8_DATA(methodClassName)); + traceMsg(comp, "\tdefiningClass %p\n", definingClass); + + if (!definingClass) + { + definingClass = std::get<1>(recv); + traceMsg(comp, "\tdefiningClass recomputed from cp as %p\n", definingClass); + } + + if (!definingClass) + { + if (aotStats) + aotStats->numDefiningClassNotFound++; + return false; + } + + J9UTF8 *className = J9ROMCLASS_CLASSNAME(TR::Compiler->cls.romClassOf((TR_OpaqueClassBlock *) definingClass)); + traceMsg(comp, "\tdefiningClass name %.*s\n", J9UTF8_LENGTH(className), J9UTF8_DATA(className)); + + // all kinds of validations may need to rely on the entire class chain, so make sure we can build one first + classChain = std::get<2>(recv); + } + + if (!classChain) + return false; + + { + // class chain and defining class found, cache here + OMR::CriticalSection classChainDataMapMonitor(clientData->getClassChainDataMapMonitor()); + classChainCache.insert(std::make_pair(definingClass, classChain)); + } + + + bool inLocalList = false; + TR::list* aotClassInfo = comp->_aotClassInfo; + if (!aotClassInfo->empty()) + { + for (auto info = aotClassInfo->begin(); info != aotClassInfo->end(); ++info) + { + TR_ASSERT(((*info)->_reloKind == TR_ValidateInstanceField || + (*info)->_reloKind == TR_ValidateStaticField || + (*info)->_reloKind == TR_ValidateClass || + (*info)->_reloKind == TR_ValidateArbitraryClass), + "TR::AOTClassInfo reloKind is not TR_ValidateInstanceField or TR_ValidateStaticField or TR_ValidateClass!"); + + if ((*info)->_reloKind == reloKind) + { + if (isStatic) + inLocalList = (TR::Compiler->cls.romClassOf((TR_OpaqueClassBlock *) definingClass) == + TR::Compiler->cls.romClassOf((TR_OpaqueClassBlock *) ((*info)->_clazz))); + else + inLocalList = (classChain == (*info)->_classChain && + cpIndex == (*info)->_cpIndex && + ramMethod == (J9Method *)(*info)->_method); + + if (inLocalList) + break; + } + } + } + + if (inLocalList) + { + traceMsg(comp, "\tFound in local list, nothing to do\n"); + if (aotStats) + { + if (isStatic) + aotStats->numStaticEntriesAlreadyStoredInLocalList++; + else + aotStats->numCHEntriesAlreadyStoredInLocalList++; + } + return true; + } + + TR::AOTClassInfo *classInfo = new (comp->trHeapMemory()) TR::AOTClassInfo(fej9, (TR_OpaqueClassBlock *)definingClass, (void *) classChain, (TR_OpaqueMethodBlock *)ramMethod, cpIndex, reloKind); + if (classInfo) + { + traceMsg(comp, "\tCreated new AOT class info %p\n", classInfo); + comp->_aotClassInfo->push_front(classInfo); + if (aotStats) + { + if (isStatic) + aotStats->numNewStaticEntriesInLocalList++; + else + aotStats->numNewCHEntriesInLocalList++; + } + + return true; + } + + // should only be a native OOM that gets us here... + return false; + } + +U_8 * +TR_ResolvedRelocatableJ9JITServerMethod::allocateException(uint32_t numBytes, TR::Compilation *comp) + { + J9JITExceptionTable *eTbl = NULL; + uint32_t size = 0; + bool shouldRetryAllocation; + eTbl = (J9JITExceptionTable *)_fe->allocateDataCacheRecord(numBytes, comp, _fe->needsContiguousCodeAndDataCacheAllocation(), &shouldRetryAllocation, + J9_JIT_DCE_EXCEPTION_INFO, &size); + if (!eTbl) + { + if (shouldRetryAllocation) + { + // force a retry + comp->failCompilation("Failed to allocate exception table"); + } + comp->failCompilation("Failed to allocate exception table"); + } + memset((uint8_t *)eTbl, 0, size); + + // These get updated in TR_RelocationRuntime::relocateAOTCodeAndData + eTbl->ramMethod = NULL; + eTbl->constantPool = NULL; + + return (U_8 *) eTbl; + } + +TR_ResolvedMethod * +TR_ResolvedRelocatableJ9JITServerMethod::createResolvedMethodFromJ9Method(TR::Compilation *comp, I_32 cpIndex, uint32_t vTableSlot, J9Method *j9method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats) { + // This method is called when a remote mirror hasn't been created yet, so it needs to be created from here. + TR_ResolvedMethod *resolvedMethod = NULL; + +#if defined(J9VM_OPT_SHARED_CLASSES) && (defined(TR_HOST_X86) || defined(TR_HOST_POWER) || defined(TR_HOST_S390) || defined(TR_HOST_ARM)) + static char *dontInline = feGetEnv("TR_AOTDontInline"); + + if (dontInline) + return NULL; + + // This message will create a remote mirror. + // Calling constructor would be simpler, but we would have to make another message to update stats + _stream->write(JITServer::MessageType::ResolvedRelocatableMethod_createResolvedRelocatableJ9Method, getRemoteMirror(), j9method, cpIndex, vTableSlot); + auto recv = _stream->read(); + auto methodInfo = std::get<0>(recv); + // These parameters are only needed to update AOT stats. + // Maybe we shouldn't even track them, because another version of this method doesn't. + bool isRomClassForMethodInSharedCache = std::get<1>(recv); + bool sameClassLoaders = std::get<2>(recv); + bool sameClass = std::get<3>(recv); + + if (std::get<0>(methodInfo).remoteMirror) + { + // Remote mirror created successfully + resolvedMethod = new (comp->trHeapMemory()) TR_ResolvedRelocatableJ9JITServerMethod((TR_OpaqueMethodBlock *) j9method, _fe, comp->trMemory(), methodInfo, this, vTableSlot); + if (aotStats) + { + aotStats->numMethodResolvedAtCompile++; + if (sameClass) + aotStats->numMethodInSameClass++; + else + aotStats->numMethodNotInSameClass++; + } + } + else if (aotStats) + { + // Remote mirror not created, update AOT stats + if (!isRomClassForMethodInSharedCache) + aotStats->numMethodROMMethodNotInSC++; + else if (!sameClassLoaders) + aotStats->numMethodFromDiffClassLoader++; + } + +#endif + + return resolvedMethod; + } + +TR_ResolvedMethod * +TR_ResolvedRelocatableJ9JITServerMethod::createResolvedMethodFromJ9Method( TR::Compilation *comp, int32_t cpIndex, uint32_t vTableSlot, J9Method *j9method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats, const TR_ResolvedJ9JITServerMethodInfo &methodInfo) + { + // If this method is called, remote mirror has either already been created or creation failed. + // In either case, methodInfo contains all parameters required to construct a resolved method or check that it's NULL. + // The only problem is that we can't update AOT stats from this method. + TR_ResolvedMethod *resolvedMethod = NULL; + if (std::get<0>(methodInfo).remoteMirror) + resolvedMethod = new (comp->trHeapMemory()) TR_ResolvedRelocatableJ9JITServerMethod((TR_OpaqueMethodBlock *) j9method, _fe, comp->trMemory(), methodInfo, this, vTableSlot); + + return resolvedMethod; + } + +TR_ResolvedMethod * +TR_ResolvedRelocatableJ9JITServerMethod::getResolvedImproperInterfaceMethod( + TR::Compilation * comp, + I_32 cpIndex) + { + if (comp->getOption(TR_UseSymbolValidationManager)) + return TR_ResolvedJ9JITServerMethod::getResolvedImproperInterfaceMethod(comp, cpIndex); + + // For now leave private and Object invokeinterface unresolved in AOT. If we + // resolve it, we may forceUnresolvedDispatch in codegen, in which case the + // generated code would attempt to resolve the wrong kind of constant pool + // entry. + return NULL; + } + +void * +TR_ResolvedRelocatableJ9JITServerMethod::startAddressForJittedMethod() + { + return NULL; + } + +void * +TR_ResolvedRelocatableJ9JITServerMethod::startAddressForJNIMethod(TR::Compilation * comp) + { +#if defined(TR_TARGET_S390) || defined(TR_TARGET_X86) || defined(TR_TARGET_POWER) + return TR_ResolvedJ9JITServerMethod::startAddressForJNIMethod(comp); +#else + return NULL; +#endif + } + +void * +TR_ResolvedRelocatableJ9JITServerMethod::startAddressForJITInternalNativeMethod() + { + return NULL; + } + +void * +TR_ResolvedRelocatableJ9JITServerMethod::startAddressForInterpreterOfJittedMethod() + { + return ((J9Method *)getNonPersistentIdentifier())->extra; + } + +TR_OpaqueClassBlock * +TR_ResolvedRelocatableJ9JITServerMethod::getClassFromConstantPool(TR::Compilation *comp, uint32_t cpIndex, bool returnClassForAOT) + { + if (returnClassForAOT || comp->getOption(TR_UseSymbolValidationManager)) + { + TR_OpaqueClassBlock * resolvedClass = TR_ResolvedJ9JITServerMethod::getClassFromConstantPool(comp, cpIndex, returnClassForAOT); + if (resolvedClass && + validateClassFromConstantPool(comp, (J9Class *)resolvedClass, cpIndex)) + { + return (TR_OpaqueClassBlock*)resolvedClass; + } + } + return NULL; + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::validateClassFromConstantPool(TR::Compilation *comp, J9Class *clazz, uint32_t cpIndex, TR_ExternalRelocationTargetKind reloKind) + { + if (comp->getOption(TR_UseSymbolValidationManager)) + { + return comp->getSymbolValidationManager()->addClassFromCPRecord(reinterpret_cast(clazz), cp(), cpIndex); + } + else + { + return storeValidationRecordIfNecessary(comp, cp(), cpIndex, reloKind, ramMethod(), clazz); + } + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::isUnresolvedString(I_32 cpIndex, bool optimizeForAOT) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); + + if (optimizeForAOT) + return TR_ResolvedJ9JITServerMethod::isUnresolvedString(cpIndex); + return true; + } + +void * +TR_ResolvedRelocatableJ9JITServerMethod::methodTypeConstant(I_32 cpIndex) + { + TR_ASSERT(false, "should be unreachable"); + return NULL; + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::isUnresolvedMethodType(I_32 cpIndex) + { + TR_ASSERT(false, "should be unreachable"); + return true; + } + +void * +TR_ResolvedRelocatableJ9JITServerMethod::methodHandleConstant(I_32 cpIndex) + { + TR_ASSERT(false, "should be unreachable"); + return NULL; + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::isUnresolvedMethodHandle(I_32 cpIndex) + { + TR_ASSERT(false, "should be unreachable"); + return true; + } + +char * +TR_ResolvedRelocatableJ9JITServerMethod::fieldOrStaticNameChars(I_32 cpIndex, int32_t & len) + { + len = 0; + return ""; + } + +TR_OpaqueClassBlock * +TR_ResolvedRelocatableJ9JITServerMethod::getDeclaringClassFromFieldOrStatic(TR::Compilation *comp, int32_t cpIndex) + { + TR_OpaqueClassBlock *definingClass = TR_ResolvedJ9JITServerMethod::getDeclaringClassFromFieldOrStatic(comp, cpIndex); + if (comp->getOption(TR_UseSymbolValidationManager)) + { + if (!comp->getSymbolValidationManager()->addDeclaringClassFromFieldOrStaticRecord(definingClass, cp(), cpIndex)) + return NULL; + } + return definingClass; + } + +TR_OpaqueClassBlock * +TR_ResolvedRelocatableJ9JITServerMethod::classOfStatic(int32_t cpIndex, bool returnClassForAOT) + { + TR_OpaqueClassBlock * clazz = TR_ResolvedJ9JITServerMethod::classOfStatic(cpIndex, returnClassForAOT); + + TR::Compilation *comp = TR::comp(); + bool validated = false; + + if (comp && comp->getOption(TR_UseSymbolValidationManager)) + { + validated = comp->getSymbolValidationManager()->addStaticClassFromCPRecord(clazz, cp(), cpIndex); + } + else + { + validated = returnClassForAOT; + } + + if (validated) + return clazz; + else + return NULL; + } + +TR_ResolvedMethod * +TR_ResolvedRelocatableJ9JITServerMethod::getResolvedPossiblyPrivateVirtualMethod( + TR::Compilation *comp, + int32_t cpIndex, + bool ignoreRtResolve, + bool * unresolvedInCP) + { + TR_ResolvedMethod *method = + TR_ResolvedJ9JITServerMethod::getResolvedPossiblyPrivateVirtualMethod( + comp, + cpIndex, + ignoreRtResolve, + unresolvedInCP); + + if (comp->getOption(TR_UseSymbolValidationManager)) + return method; + + // For now leave private invokevirtual unresolved in AOT. If we resolve it, + // we may forceUnresolvedDispatch in codegen, in which case the generated + // code would attempt to resolve the wrong kind of constant pool entry. + return (method == NULL || method->isPrivate()) ? NULL : method; + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::getUnresolvedFieldInCP(I_32 cpIndex) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); +#if defined(J9VM_INTERP_AOT_COMPILE_SUPPORT) && defined(J9VM_OPT_SHARED_CLASSES) && (defined(TR_HOST_X86) || defined(TR_HOST_POWER) || defined(TR_HOST_S390) || defined(TR_HOST_ARM)) + _stream->write(JITServer::MessageType::ResolvedMethod_getUnresolvedFieldInCP, getRemoteMirror(), cpIndex); + return std::get<0>(_stream->read()); +#else + return true; +#endif + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::getUnresolvedStaticMethodInCP(int32_t cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_getUnresolvedStaticMethodInCP, getRemoteMirror(), cpIndex); + return std::get<0>(_stream->read()); + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::getUnresolvedSpecialMethodInCP(I_32 cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedMethod_getUnresolvedSpecialMethodInCP, getRemoteMirror(), cpIndex); + return std::get<0>(_stream->read()); + } + +void +TR_ResolvedRelocatableJ9JITServerMethod::handleUnresolvedStaticMethodInCP(int32_t cpIndex, bool * unresolvedInCP) + { + *unresolvedInCP = getUnresolvedStaticMethodInCP(cpIndex); + } + +void +TR_ResolvedRelocatableJ9JITServerMethod::handleUnresolvedSpecialMethodInCP(int32_t cpIndex, bool * unresolvedInCP) + { + *unresolvedInCP = getUnresolvedSpecialMethodInCP(cpIndex); + } + +void +TR_ResolvedRelocatableJ9JITServerMethod::handleUnresolvedVirtualMethodInCP(int32_t cpIndex, bool * unresolvedInCP) + { + *unresolvedInCP = getUnresolvedVirtualMethodInCP(cpIndex); + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::getUnresolvedVirtualMethodInCP(int32_t cpIndex) + { + return false; + } + +UDATA +TR_ResolvedRelocatableJ9JITServerMethod::getFieldType(J9ROMConstantPoolItem * CP, int32_t cpIndex) + { + _stream->write(JITServer::MessageType::ResolvedRelocatableMethod_getFieldType, cpIndex, getRemoteMirror()); + auto recv = _stream->read(); + return (std::get<0>(recv)); + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::fieldAttributes(TR::Compilation * comp, int32_t cpIndex, uint32_t * fieldOffset, TR::DataType * type, bool * volatileP, bool * isFinal, bool * isPrivate, bool isStore, bool * unresolvedInCP, bool needAOTValidation) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); + + // Will search per-resolved method cache and make a remote call if needed, client will call AOT version of the method. + // We still need to create a validation record, and if it can't be added, change + // return values appropriately. + bool isStatic = false; + J9ConstantPool *constantPool = (J9ConstantPool *)literals(); + bool theFieldIsFromLocalClass = false; + TR_OpaqueClassBlock *definingClass = NULL; + TR_J9MethodFieldAttributes attributes; + if (!getCachedFieldAttributes(cpIndex, attributes, isStatic)) + { + _stream->write(JITServer::MessageType::ResolvedRelocatableMethod_fieldAttributes, getRemoteMirror(), cpIndex, isStore, needAOTValidation); + auto recv = _stream->read(); + attributes = std::get<0>(recv); + + cacheFieldAttributes(cpIndex, attributes, isStatic); + } + else + { + TR_ASSERT(validateMethodFieldAttributes(attributes, isStatic, cpIndex, isStore, constantPool), "static attributes from client and cache do not match"); + } + attributes.setMethodFieldAttributesResult(fieldOffset, type, volatileP, isFinal, isPrivate, unresolvedInCP, &theFieldIsFromLocalClass, &definingClass); + + bool fieldInfoCanBeUsed = false; + bool resolveField = true; + + if (comp->getOption(TR_DisableAOTInstanceFieldResolution)) + { + resolveField = false; + } + else + { + if (needAOTValidation) + { + if (comp->getOption(TR_UseSymbolValidationManager)) + { + fieldInfoCanBeUsed = comp->getSymbolValidationManager()->addDefiningClassFromCPRecord(reinterpret_cast (definingClass), constantPool, cpIndex); + } + else + { + fieldInfoCanBeUsed = storeValidationRecordIfNecessary(comp, constantPool, cpIndex, TR_ValidateInstanceField, ramMethod()); + } + } + else + { + fieldInfoCanBeUsed = true; + } + } + + if (!resolveField) + { + *fieldOffset = (U_32)NULL; + fieldInfoCanBeUsed = false; + } + + if (!fieldInfoCanBeUsed) + { + theFieldIsFromLocalClass = false; + // Either couldn't create validation record + // or AOT instance field resolution is disabled. + if (volatileP) *volatileP = true; + if (isFinal) *isFinal = false; + if (isPrivate) *isPrivate = false; + if (fieldOffset) *(U_32*)fieldOffset = (U_32) sizeof(J9Object); + } + + return theFieldIsFromLocalClass; + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::staticAttributes(TR::Compilation * comp, + int32_t cpIndex, + void * * address, + TR::DataType * type, + bool * volatileP, + bool * isFinal, + bool * isPrivate, + bool isStore, + bool * unresolvedInCP, + bool needAOTValidation) + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); + + // Will search per-resolved method cache and make a remote call if needed, client will call AOT version of the method. + // We still need to create a validation record, and if it can't be added, change + // return values appropriately. + bool isStatic = true; + J9ConstantPool *constantPool = (J9ConstantPool *)literals(); + bool theFieldIsFromLocalClass = false; + TR_OpaqueClassBlock *definingClass = NULL; + TR_J9MethodFieldAttributes attributes; + if(!getCachedFieldAttributes(cpIndex, attributes, isStatic)) + { + _stream->write(JITServer::MessageType::ResolvedRelocatableMethod_staticAttributes, getRemoteMirror(), cpIndex, isStore, needAOTValidation); + auto recv = _stream->read(); + attributes = std::get<0>(recv); + + cacheFieldAttributes(cpIndex, attributes, isStatic); + } + else + { + TR_ASSERT(validateMethodFieldAttributes(attributes, isStatic, cpIndex, isStore, constantPool), "static attributes from client and cache do not match"); + } + attributes.setMethodFieldAttributesResult(address, type, volatileP, isFinal, isPrivate, unresolvedInCP, &theFieldIsFromLocalClass, &definingClass); + + bool fieldInfoCanBeUsed = false; + bool resolveField = true; + if (comp->getOption(TR_DisableAOTInstanceFieldResolution)) + { + resolveField = false; + } + else + { + if (needAOTValidation) + { + if (comp->getOption(TR_UseSymbolValidationManager)) + { + fieldInfoCanBeUsed = comp->getSymbolValidationManager()->addDefiningClassFromCPRecord(reinterpret_cast (definingClass), constantPool, cpIndex, true); + } + else + { + fieldInfoCanBeUsed = storeValidationRecordIfNecessary(comp, constantPool, cpIndex, TR_ValidateInstanceField, ramMethod()); + } + } + else + { + fieldInfoCanBeUsed = true; + } + } + + if (!resolveField) + { + *address = (U_32)NULL; + fieldInfoCanBeUsed = false; + } + + if (!fieldInfoCanBeUsed) + { + theFieldIsFromLocalClass = false; + // Either couldn't create validation record + // or AOT instance field resolution is disabled. + if (volatileP) *volatileP = true; + if (isFinal) *isFinal = false; + if (isPrivate) *isPrivate = false; + if (address) *address = NULL; + } + + return theFieldIsFromLocalClass; + } + +TR_FieldAttributesCache & +TR_ResolvedRelocatableJ9JITServerMethod::getAttributesCache(bool isStatic, bool unresolvedInCP) + { + // Return persistent attributes cache for AOT compilations + TR::CompilationInfoPerThread *compInfoPT = _fe->_compInfoPT; + auto &attributesCache = isStatic ? + getJ9ClassInfo(compInfoPT, _ramClass)._staticAttributesCacheAOT : + getJ9ClassInfo(compInfoPT, _ramClass)._fieldAttributesCacheAOT; + return attributesCache; + } + +bool +TR_ResolvedRelocatableJ9JITServerMethod::validateMethodFieldAttributes(const TR_J9MethodFieldAttributes &attributes, bool isStatic, int32_t cpIndex, bool isStore, bool needAOTValidation) + { + // Validation should not be done against attributes cached per-resolved method, + // because unresolved attribute might have become resolved during the current compilation + // so validation would fail, but since it does not affect functional correctness, + // we'll just keep the cached result until the end of the compilation. + if (attributes.isUnresolvedInCP()) + return true; + if (!isStatic) + _stream->write(JITServer::MessageType::ResolvedRelocatableMethod_fieldAttributes, getRemoteMirror(), cpIndex, isStore, needAOTValidation); + else + _stream->write(JITServer::MessageType::ResolvedRelocatableMethod_staticAttributes, getRemoteMirror(), cpIndex, isStore, needAOTValidation); + auto recv = _stream->read(); + auto clientAttributes = std::get<0>(recv); + bool equal = (attributes == clientAttributes); + return equal; + } + +// TR_J9ServerMethod +static J9UTF8 *str2utf8(const char *str, int32_t length, TR_Memory *trMemory, TR_AllocationKind allocKind) + { + J9UTF8 *utf8 = (J9UTF8 *) trMemory->allocateMemory(length+sizeof(J9UTF8), allocKind); // This allocates more memory than it needs. + J9UTF8_SET_LENGTH(utf8, length); + memcpy(J9UTF8_DATA(utf8), str, length); + return utf8; + } + +TR_J9ServerMethod::TR_J9ServerMethod(TR_FrontEnd * fe, TR_Memory * trMemory, J9Class * aClazz, uintptr_t cpIndex) + : TR_J9Method() + { + TR_ASSERT(cpIndex != -1, "cpIndex shouldn't be -1"); +#if defined(JITSERVER_TODO) + TR_J9ServerVM *fej9 = (TR_J9ServerVM *) fe; + TR::CompilationInfoPerThread *compInfoPT = fej9->_compInfoPT; + std::string classNameStr; + std::string methodNameStr; + std::string methodSignatureStr; + bool cached = false; + { + // look up parameters for construction of this method in a cache first + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &cache = getJ9ClassInfo(compInfoPT, aClazz)._J9MethodNameCache; + // search the cache for existing method parameters + auto it = cache.find(cpIndex); + if (it != cache.end()) + { + const J9MethodNameAndSignature ¶ms = it->second; + classNameStr = params._classNameStr; + methodNameStr = params._methodNameStr; + methodSignatureStr = params._methodSignatureStr; + cached = true; + } + } + + if (!cached) + { + // make a remote call and cache the result + JITServer::ServerStream *stream = fej9->_compInfoPT->getMethodBeingCompiled()->_stream; + stream->write(JITServer::MessageType::get_params_to_construct_TR_j9method, aClazz, cpIndex); + const auto recv = stream->read(); + classNameStr = std::get<0>(recv); + methodNameStr = std::get<1>(recv); + methodSignatureStr = std::get<2>(recv); + + OMR::CriticalSection getRemoteROMClass(compInfoPT->getClientData()->getROMMapMonitor()); + auto &cache = getJ9ClassInfo(compInfoPT, aClazz)._J9MethodNameCache; + cache.insert({cpIndex, {classNameStr, methodNameStr, methodSignatureStr}}); + } + + _className = str2utf8((char*)&classNameStr[0], classNameStr.length(), trMemory, heapAlloc); + _name = str2utf8((char*)&methodNameStr[0], methodNameStr.length(), trMemory, heapAlloc); + _signature = str2utf8((char*)&methodSignatureStr[0], methodSignatureStr.length(), trMemory, heapAlloc); + + parseSignature(trMemory); + _fullSignature = NULL; +#endif + } + diff --git a/runtime/compiler/env/j9methodServer.hpp b/runtime/compiler/env/j9methodServer.hpp new file mode 100644 index 0000000000..164f8af194 --- /dev/null +++ b/runtime/compiler/env/j9methodServer.hpp @@ -0,0 +1,315 @@ +/******************************************************************************* + * Copyright (c) 2018, 2019 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * 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 + *******************************************************************************/ + +#ifndef J9METHODSERVER_H +#define J9METHODSERVER_H + +#include "env/j9method.h" +#include "env/PersistentCollections.hpp" +#include "runtime/JITServerIProfiler.hpp" +#include "runtime/JITClientSession.hpp" + +struct +TR_ResolvedJ9JITServerMethodInfoStruct + { + TR_ResolvedJ9Method *remoteMirror; + J9RAMConstantPoolItem *literals; + J9Class *ramClass; + uint64_t methodIndex; + uintptrj_t jniProperties; + void *jniTargetAddress; + bool isInterpreted; + bool isJNINative; + bool isMethodInValidLibrary; + TR::RecognizedMethod mandatoryRm; + TR::RecognizedMethod rm; + void *startAddressForJittedMethod; + bool virtualMethodIsOverridden; + void *addressContainingIsOverriddenBit; + J9ClassLoader *classLoader; + }; + + +// The last 3 strings are serialized versions of jittedBodyInfo, persistentMethodInfo and TR_ContiguousIPMethodHashTableInfo +using TR_ResolvedJ9JITServerMethodInfo = std::tuple; + +// key used to identify a resolved method in resolved methods cache. +// Since one cache contains different types of resolved methods, need to uniquely identify +// each type. Apparently, the same cpIndex may refer to both virtual or special method, +// for different method calls, so using TR_ResolvedMethodType is necessary. +enum TR_ResolvedMethodType {VirtualFromCP, VirtualFromOffset, Interface, Static, Special, ImproperInterface, NoType}; +struct +TR_ResolvedMethodKey + { + TR_ResolvedMethodType type; + TR_OpaqueClassBlock *ramClass; + int32_t cpIndex; + TR_OpaqueClassBlock *classObject; // only set for resolved interface methods + + bool operator==(const TR_ResolvedMethodKey &other) const + { + return + type == other.type && + ramClass == other.ramClass && + cpIndex == other.cpIndex && + classObject == other.classObject; + } + }; + + +struct TR_IsUnresolvedString + { + TR_IsUnresolvedString(): + _optimizeForAOTTrueResult(false), + _optimizeForAOTFalseResult(false) + {} + TR_IsUnresolvedString(bool optimizeForAOTTrueResult, bool optimizeForAOTFalseResult): + _optimizeForAOTTrueResult(optimizeForAOTTrueResult), + _optimizeForAOTFalseResult(optimizeForAOTFalseResult) + {} + bool _optimizeForAOTTrueResult; + bool _optimizeForAOTFalseResult; + }; + +// define a hash function for TR_ResolvedMethodKey +namespace std + { + template <> + struct hash + { + std::size_t operator()(const TR_ResolvedMethodKey &k) const + { + // Compute a hash by hashing each part separately and then XORing them. + return + std::hash()(static_cast(k.type)) ^ + std::hash()(k.ramClass) ^ + std::hash()(k.cpIndex) ^ + std::hash()(k.classObject); + } + }; + } + +struct +TR_ResolvedMethodCacheEntry + { + TR_OpaqueMethodBlock *method; + uint32_t vTableSlot; + TR_ResolvedJ9JITServerMethodInfo methodInfo; + }; + +using TR_ResolvedMethodInfoCache = UnorderedMap; + +/** + * @class TR_ResolvedJ9JITServerMethod + * @brief Class used by JITServer for obtaining method/class information needed + * during compilation from JITClient + * + * This class is an extension of the TR_ResolvedJ9Method class. Most of the APIs of + * TR_ResolvedJ9JITServerMethod are remote calls to the JITClient to obtain + * compilation information about the compiling method and related classes. Upon + * instantiation of a TR_ResolvedJ9JITServerMethod class, a mirror TR_ResolvedJ9Method + * will be created on the client via TR_ResolvedJ9JITServerMethod::createResolvedMethodMirror. + * During compilation the JITServer will mostly be communicating with the client-side + * mirror instance. Certain data are cached at the JITServer to reduce the number + * of remote calls to JITClient. + */ + +class TR_ResolvedJ9JITServerMethod : public TR_ResolvedJ9Method + { +public: + TR_ResolvedJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd *, TR_Memory *, TR_ResolvedMethod * owningMethod = 0, uint32_t vTableSlot = 0); + TR_ResolvedJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd *, TR_Memory *, const TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_ResolvedMethod * owningMethod = 0, uint32_t vTableSlot = 0); + + virtual J9ROMClass *romClassPtr() override; + virtual J9RAMConstantPoolItem *literals() override; + virtual J9Class *constantPoolHdr() override; + virtual bool isJNINative() override; + virtual bool isInterpreted() override { return _isInterpreted; }; + virtual bool isMethodInValidLibrary() override { return _isMethodInValidLibrary; }; + virtual bool shouldFailSetRecognizedMethodInfoBecauseOfHCR() override; + virtual void setRecognizedMethodInfo(TR::RecognizedMethod rm) override; + virtual J9ClassLoader *getClassLoader() override; + virtual bool staticAttributes( TR::Compilation *, int32_t cpIndex, void * *, TR::DataType * type, bool * volatileP, bool * isFinal, bool *isPrivate, bool isStore, bool * unresolvedInCP, bool needsAOTValidation) override; + virtual TR_OpaqueClassBlock * getClassFromConstantPool( TR::Compilation *, uint32_t cpIndex, bool returnClassToAOT = false) override; + virtual TR_OpaqueClassBlock * getDeclaringClassFromFieldOrStatic( TR::Compilation *comp, int32_t cpIndex) override; + virtual TR_OpaqueClassBlock * classOfStatic(int32_t cpIndex, bool returnClassForAOT = false) override; + virtual bool isConstantDynamic(I_32 cpIndex) override; + virtual bool isUnresolvedString(int32_t cpIndex, bool optimizeForAOT = false) override; + virtual TR_ResolvedMethod * getResolvedVirtualMethod( TR::Compilation *, int32_t cpIndex, bool ignoreRtResolve, bool * unresolvedInCP) override; + virtual TR_ResolvedMethod * getResolvedPossiblyPrivateVirtualMethod( TR::Compilation *, int32_t cpIndex, bool ignoreRtResolve, bool * unresolvedInCP) override; + virtual TR_ResolvedMethod * getResolvedStaticMethod(TR::Compilation * comp, I_32 cpIndex, bool * unresolvedInCP) override; + virtual TR_ResolvedMethod * getResolvedSpecialMethod(TR::Compilation * comp, I_32 cpIndex, bool * unresolvedInCP) override; + virtual TR_ResolvedMethod * createResolvedMethodFromJ9Method( TR::Compilation *comp, int32_t cpIndex, uint32_t vTableSlot, J9Method *j9Method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats) override; + virtual TR_ResolvedMethod * createResolvedMethodFromJ9Method( TR::Compilation *comp, int32_t cpIndex, uint32_t vTableSlot, J9Method *j9Method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats, const TR_ResolvedJ9JITServerMethodInfo &methodInfo); + virtual uint32_t classCPIndexOfMethod(uint32_t methodCPIndex) override; + virtual bool fieldsAreSame(int32_t cpIndex1, TR_ResolvedMethod *m2, int32_t cpIndex2, bool &sigSame) override; + virtual bool staticsAreSame(int32_t cpIndex1, TR_ResolvedMethod *m2, int32_t cpIndex2, bool &sigSame) override; + virtual bool fieldAttributes(TR::Compilation *, int32_t cpIndex, uint32_t * fieldOffset, TR::DataType * type, bool * volatileP, bool * isFinal, bool *isPrivate, bool isStore, bool * unresolvedInCP, bool needsAOTValidation) override; + virtual void * startAddressForJittedMethod() override; + virtual char * localName(uint32_t slotNumber, uint32_t bcIndex, int32_t &len, TR_Memory *) override; + virtual bool virtualMethodIsOverridden() override { return _virtualMethodIsOverridden; } + virtual TR_ResolvedMethod * getResolvedInterfaceMethod(TR::Compilation *, TR_OpaqueClassBlock * classObject, int32_t cpIndex) override; + virtual TR_OpaqueClassBlock * getResolvedInterfaceMethod(int32_t cpIndex, uintptrj_t * pITableIndex) override; + virtual uint32_t getResolvedInterfaceMethodOffset(TR_OpaqueClassBlock * classObject, int32_t cpIndex) override; + virtual TR_ResolvedMethod * getResolvedImproperInterfaceMethod(TR::Compilation * comp, I_32 cpIndex) override; + virtual void * startAddressForJNIMethod(TR::Compilation *) override; + virtual void *startAddressForInterpreterOfJittedMethod() override; + virtual TR_ResolvedMethod *getResolvedVirtualMethod(TR::Compilation * comp, TR_OpaqueClassBlock * classObject, I_32 virtualCallOffset , bool ignoreRtResolve) override; + virtual char * fieldOrStaticSignatureChars(I_32 cpIndex, int32_t & len) override; + virtual char * getClassNameFromConstantPool(uint32_t cpIndex, uint32_t &length) override; + virtual char * classNameOfFieldOrStatic(int32_t cpIndex, int32_t & len) override; + virtual char * classSignatureOfFieldOrStatic(int32_t cpIndex, int32_t & len) override; + virtual char * fieldOrStaticNameChars(int32_t cpIndex, int32_t & len) override; + virtual bool isSubjectToPhaseChange(TR::Compilation *comp) override; + virtual void * stringConstant(int32_t cpIndex) override; + virtual TR_ResolvedMethod *getResolvedHandleMethod( TR::Compilation *, int32_t cpIndex, bool * unresolvedInCP) override; +#if defined(J9VM_OPT_REMOVE_CONSTANT_POOL_SPLITTING) + virtual bool isUnresolvedMethodTypeTableEntry(int32_t cpIndex) override; + virtual void * methodTypeTableEntryAddress(int32_t cpIndex) override; +#endif + virtual bool isUnresolvedCallSiteTableEntry(int32_t callSiteIndex) override; + virtual void * callSiteTableEntryAddress(int32_t callSiteIndex) override; + virtual bool isUnresolvedVarHandleMethodTypeTableEntry(int32_t cpIndex) override; + virtual void * varHandleMethodTypeTableEntryAddress(int32_t cpIndex) override; + virtual TR_ResolvedMethod * getResolvedDynamicMethod( TR::Compilation *, int32_t cpIndex, bool * unresolvedInCP) override; + virtual bool isSameMethod(TR_ResolvedMethod *) override; + virtual bool isInlineable(TR::Compilation *) override; + virtual void setWarmCallGraphTooBig(uint32_t, TR::Compilation *) override; + virtual void setVirtualMethodIsOverridden() override; + virtual void * addressContainingIsOverriddenBit() override { return _addressContainingIsOverriddenBit; } + virtual bool methodIsNotzAAPEligible() override; + virtual void setClassForNewInstance(J9Class *c) override; + virtual TR_OpaqueClassBlock * classOfMethod() override; + virtual TR_PersistentJittedBodyInfo *getExistingJittedBodyInfo() override; + virtual void getFaninInfo(uint32_t *count, uint32_t *weight, uint32_t *otherBucketWeight = NULL) override; + virtual bool getCallerWeight(TR_ResolvedJ9Method *caller, uint32_t *weight, uint32_t pcIndex=~0) override; + virtual uint16_t archetypeArgPlaceholderSlot() override; + + TR_ResolvedJ9Method *getRemoteMirror() const { return _remoteMirror; } + bool inROMClass(void *address); + static void createResolvedMethodMirror(TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_OpaqueMethodBlock *method, uint32_t vTableSlot, TR_ResolvedMethod *owningMethod, TR_FrontEnd *fe, TR_Memory *trMemory); + static void createResolvedMethodFromJ9MethodMirror(TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_OpaqueMethodBlock *method, uint32_t vTableSlot, TR_ResolvedMethod *owningMethod, TR_FrontEnd *fe, TR_Memory *trMemory); + bool addValidationRecordForCachedResolvedMethod(const TR_ResolvedMethodKey &key, TR_OpaqueMethodBlock *method); + void cacheResolvedMethodsCallees(); + +protected: + JITServer::ServerStream *_stream; + J9Class *_ramClass; // client pointer to RAM class + static void packMethodInfo(TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_ResolvedJ9Method *resolvedMethod, TR_FrontEnd *fe); + static void setAttributeResultFromResolvedMethodFieldAttributes(const TR_J9MethodFieldAttributes &attributes, U_32 * fieldOffset, void **address, TR::DataType * type, bool * volatileP, bool * isFinal, bool * isPrivate, bool * unresolvedInCP, bool *result, bool isStatic); + virtual bool getCachedFieldAttributes(int32_t cpIndex, TR_J9MethodFieldAttributes &attributes, bool isStatic); + virtual void cacheFieldAttributes(int32_t cpIndex, const TR_J9MethodFieldAttributes &attributes, bool isStatic); + virtual TR_FieldAttributesCache &getAttributesCache(bool isStatic, bool unresolvedInCP=false); + virtual bool validateMethodFieldAttributes(const TR_J9MethodFieldAttributes &attributes, bool isStatic, int32_t cpIndex, bool isStore, bool needAOTValidation); + virtual bool canCacheFieldAttributes(int32_t cpIndex, const TR_J9MethodFieldAttributes &attributes, bool isStatic); + +private: + + J9ROMClass *_romClass; // cached copy of ROM class from client + J9RAMConstantPoolItem *_literals; // client pointer to constant pool + TR_ResolvedJ9Method *_remoteMirror; + void *_startAddressForJittedMethod; // JIT entry point + void *_addressContainingIsOverriddenBit; // Only valid at the client. Cached info from the client + J9ClassLoader *_classLoader; // class loader for the class of this method; only valid at the client + bool _isInterpreted; // cached information coming from client + bool _isJNINative; + bool _isMethodInValidLibrary; + bool _virtualMethodIsOverridden; // cached information coming from client + TR_PersistentJittedBodyInfo *_bodyInfo; // cached info coming from the client; uses heap memory + // If method is not yet compiled this is null + TR_IPMethodHashTableEntry *_iProfilerMethodEntry; + + char* getROMString(int32_t& len, void *basePtr, std::initializer_list offsets); + char* getRemoteROMString(int32_t& len, void *basePtr, std::initializer_list offsets); + virtual char * fieldOrStaticName(I_32 cpIndex, int32_t & len, TR_Memory * trMemory, TR_AllocationKind kind = heapAlloc) override; + void unpackMethodInfo(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd * fe, TR_Memory * trMemory, uint32_t vTableSlot, TR::CompilationInfoPerThread *threadCompInfo, const TR_ResolvedJ9JITServerMethodInfo &methodInfo); + }; + + +/** + * @class TR_ResolvedRelocatableJ9JITServerMethod + * @brief Class used by JITServer for obtaining method/class information needed + * during compilation from JITClient plus additional handling for AOT compilations + * + * This class is an extension of the above TR_ResolvedJ9JITServerMethod class that + * has additional handling for AOT compilations. The relationship between + * TR_ResolvedJ9JITServerMethod and TR_ResolvedRelocatableJ9JITServerMethod is similar + * to the relationship between TR_ResolvedJ9Method and TR_ResolvedRelocatableJ9Method. + */ + +class TR_ResolvedRelocatableJ9JITServerMethod : public TR_ResolvedJ9JITServerMethod + { + public: + TR_ResolvedRelocatableJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd *, TR_Memory *, TR_ResolvedMethod * owningMethod = 0, uint32_t vTableSlot = 0); + TR_ResolvedRelocatableJ9JITServerMethod(TR_OpaqueMethodBlock * aMethod, TR_FrontEnd *, TR_Memory *, const TR_ResolvedJ9JITServerMethodInfo &methodInfo, TR_ResolvedMethod * owningMethod = 0, uint32_t vTableSlot = 0); + + virtual void * constantPool() override; + virtual bool isInterpreted() override; + virtual bool isInterpretedForHeuristics() override; + + virtual void * startAddressForJittedMethod() override; + virtual void * startAddressForJNIMethod( TR::Compilation *) override; + virtual void * startAddressForJITInternalNativeMethod() override; + virtual void * startAddressForInterpreterOfJittedMethod() override; + + virtual TR_OpaqueClassBlock * getClassFromConstantPool( TR::Compilation *, uint32_t cpIndex, bool returnClassToAOT = false) override; + virtual bool validateClassFromConstantPool( TR::Compilation *comp, J9Class *clazz, uint32_t cpIndex, TR_ExternalRelocationTargetKind reloKind = TR_ValidateClass) override; + virtual bool validateArbitraryClass( TR::Compilation *comp, J9Class *clazz) override; + virtual bool isUnresolvedString(int32_t cpIndex, bool optimizeForAOT = false) override; + virtual void * methodTypeConstant(int32_t cpIndex) override; + virtual bool isUnresolvedMethodType(int32_t cpIndex) override; + virtual void * methodHandleConstant(int32_t cpIndex) override; + virtual bool isUnresolvedMethodHandle(int32_t cpIndex) override; + virtual TR_OpaqueClassBlock * classOfStatic(int32_t cpIndex, bool returnClassForAOT = false) override; + virtual TR_ResolvedMethod * getResolvedPossiblyPrivateVirtualMethod( TR::Compilation *, int32_t cpIndex, bool ignoreRtResolve, bool * unresolvedInCP) override; + virtual bool getUnresolvedFieldInCP(I_32 cpIndex) override; + virtual bool getUnresolvedStaticMethodInCP(int32_t cpIndex) override; + virtual bool getUnresolvedSpecialMethodInCP(I_32 cpIndex) override; + virtual bool getUnresolvedVirtualMethodInCP(int32_t cpIndex) override; + /* No need to override the stringConstant method as the parent method will be sufficient */ + virtual bool fieldAttributes(TR::Compilation * comp, int32_t cpIndex, uint32_t * fieldOffset, TR::DataType * type, bool * volatileP, bool * isFinal, bool * isPrivate, bool isStore, bool * unresolvedInCP, bool needAOTValidation) override; + virtual bool staticAttributes(TR::Compilation * comp, int32_t cpIndex, void * * address,TR::DataType * type, bool * volatileP, bool * isFinal, bool * isPrivate, bool isStore, bool * unresolvedInCP, bool needAOTValidation); + virtual TR_ResolvedMethod * getResolvedImproperInterfaceMethod(TR::Compilation * comp, I_32 cpIndex) override; + + virtual TR_OpaqueMethodBlock *getNonPersistentIdentifier() override; + virtual uint8_t * allocateException(uint32_t, TR::Compilation*) override; + virtual TR_OpaqueClassBlock *getDeclaringClassFromFieldOrStatic( TR::Compilation *comp, int32_t cpIndex) override; + bool storeValidationRecordIfNecessary(TR::Compilation * comp, J9ConstantPool *constantPool, int32_t cpIndex, TR_ExternalRelocationTargetKind reloKind, J9Method *ramMethod, J9Class *definingClass=0); + +protected: + virtual TR_ResolvedMethod * createResolvedMethodFromJ9Method(TR::Compilation *comp, int32_t cpIndex, uint32_t vTableSlot, J9Method *j9Method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats) override; + virtual TR_ResolvedMethod * createResolvedMethodFromJ9Method( TR::Compilation *comp, int32_t cpIndex, uint32_t vTableSlot, J9Method *j9Method, bool * unresolvedInCP, TR_AOTInliningStats *aotStats, const TR_ResolvedJ9JITServerMethodInfo &methodInfo) override; + virtual void handleUnresolvedStaticMethodInCP(int32_t cpIndex, bool * unresolvedInCP) override; + virtual void handleUnresolvedSpecialMethodInCP(int32_t cpIndex, bool * unresolvedInCP) override; + virtual void handleUnresolvedVirtualMethodInCP(int32_t cpIndex, bool * unresolvedInCP) override; + virtual char * fieldOrStaticNameChars(int32_t cpIndex, int32_t & len) override; + virtual TR_FieldAttributesCache &getAttributesCache(bool isStatic, bool unresolvedInCP=false) override; + virtual bool validateMethodFieldAttributes(const TR_J9MethodFieldAttributes &attributes, bool isStatic, int32_t cpIndex, bool isStore, bool needAOTValidation) override; + UDATA getFieldType(J9ROMConstantPoolItem * CP, int32_t cpIndex); + }; + +class TR_J9ServerMethod : public TR_J9Method + { +public: + TR_J9ServerMethod(TR_FrontEnd *trvm, TR_Memory *, J9Class * aClazz, uintptr_t cpIndex); + }; +#endif // J9METHODSERVER_H diff --git a/runtime/compiler/runtime/IProfiler.hpp b/runtime/compiler/runtime/IProfiler.hpp index a18ab36897..9a32770059 100644 --- a/runtime/compiler/runtime/IProfiler.hpp +++ b/runtime/compiler/runtime/IProfiler.hpp @@ -92,6 +92,7 @@ class TR_ResolvedMethod; class TR_AbstractInfo; class TR_BitVector; class TR_J9VMBase; +class TR_J9SharedCache; #if defined (_MSC_VER) diff --git a/runtime/compiler/runtime/JITClientSession.cpp b/runtime/compiler/runtime/JITClientSession.cpp index 9fbcc67a67..a72300ffd2 100644 --- a/runtime/compiler/runtime/JITClientSession.cpp +++ b/runtime/compiler/runtime/JITClientSession.cpp @@ -277,76 +277,48 @@ ClientSessionData::printStats() j9tty_printf(PORTLIB, "\tTotal size of cached ROM classes + methods: %d bytes\n", total); } +ClientSessionData::ClassInfo::ClassInfo() : + _romClass(NULL), + _remoteRomClass(NULL), + _methodsOfClass(NULL), + _baseComponentClass(NULL), + _numDimensions(0), + _remoteROMStringsCache(decltype(_remoteROMStringsCache)::allocator_type(TR::Compiler->persistentAllocator())), + _fieldOrStaticNameCache(decltype(_fieldOrStaticNameCache)::allocator_type(TR::Compiler->persistentAllocator())), + _parentClass(NULL), + _interfaces(NULL), + _classHasFinalFields(false), + _classDepthAndFlags(0), + _classInitialized(false), + _byteOffsetToLockword(0), + _leafComponentClass(NULL), + _classLoader(NULL), + _hostClass(NULL), + _componentClass(NULL), + _arrayClass(NULL), + _totalInstanceSize(0), + _classOfStaticCache(decltype(_classOfStaticCache)::allocator_type(TR::Compiler->persistentAllocator())), + _constantClassPoolCache(decltype(_constantClassPoolCache)::allocator_type(TR::Compiler->persistentAllocator())), + _fieldAttributesCache(decltype(_fieldAttributesCache)::allocator_type(TR::Compiler->persistentAllocator())), + _staticAttributesCache(decltype(_staticAttributesCache)::allocator_type(TR::Compiler->persistentAllocator())), + _fieldAttributesCacheAOT(decltype(_fieldAttributesCacheAOT)::allocator_type(TR::Compiler->persistentAllocator())), + _staticAttributesCacheAOT(decltype(_fieldAttributesCacheAOT)::allocator_type(TR::Compiler->persistentAllocator())), + _constantPool(NULL), + _jitFieldsCache(decltype(_jitFieldsCache)::allocator_type(TR::Compiler->persistentAllocator())), + _classFlags(0), + _fieldOrStaticDeclaringClassCache(decltype(_fieldOrStaticDeclaringClassCache)::allocator_type(TR::Compiler->persistentAllocator())), + _J9MethodNameCache(decltype(_J9MethodNameCache)::allocator_type(TR::Compiler->persistentAllocator())) + { + } + void ClientSessionData::ClassInfo::freeClassInfo() { TR_Memory::jitPersistentFree(_romClass); - // If string cache exists, free it - if (_remoteROMStringsCache) - { - _remoteROMStringsCache->~PersistentUnorderedMap(); - jitPersistentFree(_remoteROMStringsCache); - } - // if fieldOrStaticNameCache exists, free it - if (_fieldOrStaticNameCache) - { - _fieldOrStaticNameCache->~PersistentUnorderedMap(); - jitPersistentFree(_fieldOrStaticNameCache); - } // free cached _interfaces _interfaces->~PersistentVector(); jitPersistentFree(_interfaces); - - // if class of static cache exists, free it - if (_classOfStaticCache) - { - _classOfStaticCache->~PersistentUnorderedMap(); - jitPersistentFree(_classOfStaticCache); - } - - if (_constantClassPoolCache) - { - _constantClassPoolCache->~PersistentUnorderedMap(); - jitPersistentFree(_constantClassPoolCache); - } - - if (_fieldAttributesCache) - { - _fieldAttributesCache->~TR_FieldAttributesCache(); - jitPersistentFree(_fieldAttributesCache); - } - if (_staticAttributesCache) - { - _staticAttributesCache->~TR_FieldAttributesCache(); - jitPersistentFree(_staticAttributesCache); - } - - if (_fieldAttributesCacheAOT) - { - _fieldAttributesCacheAOT->~TR_FieldAttributesCache(); - jitPersistentFree(_fieldAttributesCacheAOT); - } - if (_staticAttributesCacheAOT) - { - _staticAttributesCacheAOT->~TR_FieldAttributesCache(); - jitPersistentFree(_staticAttributesCacheAOT); - } - if (_jitFieldsCache) - { - _jitFieldsCache->~TR_JitFieldsCache(); - jitPersistentFree(_jitFieldsCache); - } - if (_fieldOrStaticDeclaringClassCache) - { - _fieldOrStaticDeclaringClassCache->~PersistentUnorderedMap(); - jitPersistentFree(_fieldOrStaticDeclaringClassCache); - } - if (_J9MethodNameCache) - { - _J9MethodNameCache->~PersistentUnorderedMap(); - jitPersistentFree(_J9MethodNameCache); - } } ClientSessionData::VMInfo * diff --git a/runtime/compiler/runtime/JITClientSession.hpp b/runtime/compiler/runtime/JITClientSession.hpp index 2cfdbd55af..b9b38c8cab 100644 --- a/runtime/compiler/runtime/JITClientSession.hpp +++ b/runtime/compiler/runtime/JITClientSession.hpp @@ -228,6 +228,7 @@ class ClientSessionData struct ClassInfo { + ClassInfo(); void freeClassInfo(); // this method is in place of a destructor. We can't have destructor // because it would be called after inserting ClassInfo into the ROM map, freeing romClass J9ROMClass *_romClass; // romClass content exists in persistentMemory at the server @@ -236,8 +237,8 @@ class ClientSessionData // Fields meaningful for arrays TR_OpaqueClassBlock *_baseComponentClass; int32_t _numDimensions; - PersistentUnorderedMap *_remoteROMStringsCache; // cached strings from the client - PersistentUnorderedMap *_fieldOrStaticNameCache; + PersistentUnorderedMap _remoteROMStringsCache; // cached strings from the client + PersistentUnorderedMap _fieldOrStaticNameCache; TR_OpaqueClassBlock *_parentClass; PersistentVector *_interfaces; bool _classHasFinalFields; @@ -250,20 +251,19 @@ class ClientSessionData TR_OpaqueClassBlock * _componentClass; // caching the componentType of the J9ArrayClass TR_OpaqueClassBlock * _arrayClass; uintptrj_t _totalInstanceSize; - PersistentUnorderedMap *_classOfStaticCache; - PersistentUnorderedMap *_constantClassPoolCache; - TR_FieldAttributesCache *_fieldAttributesCache; - TR_FieldAttributesCache *_staticAttributesCache; - TR_FieldAttributesCache *_fieldAttributesCacheAOT; - TR_FieldAttributesCache *_staticAttributesCacheAOT; + PersistentUnorderedMap _classOfStaticCache; + PersistentUnorderedMap _constantClassPoolCache; + TR_FieldAttributesCache _fieldAttributesCache; + TR_FieldAttributesCache _staticAttributesCache; + TR_FieldAttributesCache _fieldAttributesCacheAOT; + TR_FieldAttributesCache _staticAttributesCacheAOT; J9ConstantPool *_constantPool; - TR_JitFieldsCache *_jitFieldsCache; + TR_JitFieldsCache _jitFieldsCache; uintptrj_t _classFlags; - PersistentUnorderedMap *_fieldOrStaticDeclaringClassCache; - PersistentUnorderedMap *_J9MethodNameCache; // key is a cpIndex + PersistentUnorderedMap _fieldOrStaticDeclaringClassCache; + PersistentUnorderedMap _J9MethodNameCache; // key is a cpIndex }; // struct ClassInfo - /** @class J9MethodInfo @brief Struct that holds relevant data for a j9method @@ -467,3 +467,4 @@ class ClientSessionHT }; // class ClientSessionHT #endif /* defined(JIT_CLIENT_SESSION_H) */ + diff --git a/runtime/compiler/runtime/JITServerIProfiler.cpp b/runtime/compiler/runtime/JITServerIProfiler.cpp index 572086619a..59738756d8 100644 --- a/runtime/compiler/runtime/JITServerIProfiler.cpp +++ b/runtime/compiler/runtime/JITServerIProfiler.cpp @@ -23,6 +23,7 @@ #include "runtime/JITServerIProfiler.hpp" #include "control/CompilationRuntime.hpp" #include "control/JITServerCompilationThread.hpp" +#include "env/j9methodServer.hpp" #include "runtime/JITClientSession.hpp" #include "infra/CriticalSection.hpp" // for OMR::CriticalSection #include "ilgen/J9ByteCode.hpp" @@ -577,10 +578,8 @@ JITServerIProfiler::persistIprofileInfo(TR::ResolvedMethodSymbol *methodSymbol, if (clientSessionData->getOrCacheVMInfo(stream)->_elgibleForPersistIprofileInfo) { -#if defined(JITSERVER_TODO) auto serverMethod = static_cast(method); compInfoPT->cacheResolvedMirrorMethodsPersistIPInfo(serverMethod->getRemoteMirror()); -#endif } } diff --git a/runtime/compiler/x/runtime/Recomp.cpp b/runtime/compiler/x/runtime/Recomp.cpp index 4e0997cef6..db6adcadb1 100644 --- a/runtime/compiler/x/runtime/Recomp.cpp +++ b/runtime/compiler/x/runtime/Recomp.cpp @@ -29,6 +29,7 @@ #include "runtime/J9Runtime.hpp" #include "x/runtime/X86Runtime.hpp" #include "env/VMJ9.h" +#include "control/CompilationRuntime.hpp" #if defined(TR_HOST_X86) && defined(TR_HOST_64BIT) #define IS_32BIT_RIP(x,rip) ((intptrj_t)(x) == (intptrj_t)(rip) + (int32_t)((intptrj_t)(x) - (intptrj_t)(rip))) @@ -104,6 +105,17 @@ extern "C" void mcc_AMD64callPointPatching_unwrapper(void **argsPtr, void **resP // TR_PersistentJittedBodyInfo *J9::Recompilation::getJittedBodyInfoFromPC(void *startPC) { +#if defined(JITSERVER_SUPPORT) + if (auto stream = TR::CompilationInfo::getStream()) + { + TR_ASSERT(TR::comp(), "Must be used during compilation when calling getJittedBodyInfoFromPC on the server"); + stream->write(JITServer::MessageType::Recompilation_getJittedBodyInfoFromPC, startPC); + auto recv = stream->read(); + auto &bodyInfoStr = std::get<0>(recv); + auto &methodInfoStr = std::get<1>(recv); + return J9::Recompilation::persistentJittedBodyInfoFromString(bodyInfoStr, methodInfoStr, TR::comp()->trMemory()); + } +#endif // The body info pointer is stored in the pre-prologue of the method. The // location of the field depends upon the type of the method header. Use the // header-type bits in the linkage info fields to determine what kind of header