diff --git a/runtime/compiler/control/CompilationThread.cpp b/runtime/compiler/control/CompilationThread.cpp index 902593b910e..48155ffa8d4 100644 --- a/runtime/compiler/control/CompilationThread.cpp +++ b/runtime/compiler/control/CompilationThread.cpp @@ -59,6 +59,7 @@ #include "env/StackMemoryRegion.hpp" #include "env/jittypes.h" #include "env/ClassTableCriticalSection.hpp" +#include "env/DependencyTable.hpp" #include "env/PersistentCHTable.hpp" #include "env/VMAccessCriticalSection.hpp" #include "env/VerboseLog.hpp" @@ -8253,6 +8254,9 @@ TR::CompilationInfoPerThreadBase::compile(J9VMThread * vmThread, vmThread->omrVMThread->vmState = J9VMSTATE_JIT | J9VMSTATE_MINOR; vmThread->jitMethodToBeCompiled = method; + if (auto dependencyTable = getCompilationInfo()->getPersistentInfo()->getAOTDependencyTable()) + dependencyTable->methodWillBeCompiled(method); + try { TR::RawAllocator rawAllocator(vmThread->javaVM); diff --git a/runtime/compiler/control/DLLMain.cpp b/runtime/compiler/control/DLLMain.cpp index ad9b464e958..0f0cd1f63e0 100644 --- a/runtime/compiler/control/DLLMain.cpp +++ b/runtime/compiler/control/DLLMain.cpp @@ -526,6 +526,8 @@ IDATA J9VMDllMain(J9JavaVM* vm, IDATA stage, void * reserved) persistentInfo->setAOTDependencyTable(dependencyTable); } #endif /* !defined(PERSISTENT_COLLECTIONS_UNSUPPORTED) */ + if (!persistentInfo->getAOTDependencyTable()) + persistentInfo->setTrackAOTDependencies(false); } } else diff --git a/runtime/compiler/control/HookedByTheJit.cpp b/runtime/compiler/control/HookedByTheJit.cpp index 966c91dca83..4a0a288a7e8 100644 --- a/runtime/compiler/control/HookedByTheJit.cpp +++ b/runtime/compiler/control/HookedByTheJit.cpp @@ -566,20 +566,30 @@ static void jitHookInitializeSendTarget(J9HookInterface * * hook, UDATA eventNum #endif /* defined(J9VM_OPT_CRIU_SUPPORT) */ ) { - int32_t scount = optionsAOT->getInitialSCount(); - uint16_t newScount = 0; - if (sc && sc->isHint(method, TR_HintFailedValidation, &newScount)) + if (auto dependencyTable = compInfo->getPersistentInfo()->getAOTDependencyTable()) { - if ((scount == TR_QUICKSTART_INITIAL_SCOUNT) || (scount == TR_INITIAL_SCOUNT)) - { // If scount is not user specified (coarse way due to info being lost from options parsing) - // TODO: Is casting the best thing to do here? - scount= std::min(getCount(romMethod, optionsJIT, optionsAOT), static_cast(newScount) ); // Find what would've been normal count for this method and - // make sure new scount isn't larger than that - if (optionsAOT->getVerboseOption(TR_VerboseSCHints) || optionsJIT->getVerboseOption(TR_VerboseSCHints)) - TR_VerboseLog::writeLineLocked(TR_Vlog_SCHINTS,"Found hint in sc, increase scount to: %d, wanted scount: %d", scount, newScount); + bool dependenciesSatisfied = false; + if (dependencyTable->trackMethod(vmThread, method, romMethod, dependenciesSatisfied)) + count = dependenciesSatisfied ? 0 : 3003; // TODO: figure out what this should be + } + + if (count == -1) + { + int32_t scount = optionsAOT->getInitialSCount(); + uint16_t newScount = 0; + if (sc && sc->isHint(method, TR_HintFailedValidation, &newScount)) + { + if ((scount == TR_QUICKSTART_INITIAL_SCOUNT) || (scount == TR_INITIAL_SCOUNT)) + { // If scount is not user specified (coarse way due to info being lost from options parsing) + // TODO: Is casting the best thing to do here? + scount= std::min(getCount(romMethod, optionsJIT, optionsAOT), static_cast(newScount) ); // Find what would've been normal count for this method and + // make sure new scount isn't larger than that + if (optionsAOT->getVerboseOption(TR_VerboseSCHints) || optionsJIT->getVerboseOption(TR_VerboseSCHints)) + TR_VerboseLog::writeLineLocked(TR_Vlog_SCHINTS,"Found hint in sc, increase scount to: %d, wanted scount: %d", scount, newScount); + } } + count = scount; } - count = scount; compInfo->incrementNumMethodsFoundInSharedCache(); } // AOT Body not in SCC, so scount was not set diff --git a/runtime/compiler/env/DependencyTable.cpp b/runtime/compiler/env/DependencyTable.cpp index 6569df4aa0f..d3615318c27 100644 --- a/runtime/compiler/env/DependencyTable.cpp +++ b/runtime/compiler/env/DependencyTable.cpp @@ -21,6 +21,7 @@ *******************************************************************************/ +#include #include "control/CompilationThread.hpp" #include "env/DependencyTable.hpp" #include "env/J9SharedCache.hpp" @@ -32,9 +33,126 @@ TR_AOTDependencyTable::TR_AOTDependencyTable(TR_J9SharedCache *sharedCache) : _isActive(true), _sharedCache(sharedCache), _tableMonitor(TR::Monitor::create("JIT-AOTDependencyTableMonitor")), - _offsetMap(decltype(_offsetMap)::allocator_type(TR::Compiler->persistentAllocator())) + _offsetMap(decltype(_offsetMap)::allocator_type(TR::Compiler->persistentAllocator())), + _methodMap(decltype(_methodMap)::allocator_type(TR::Compiler->persistentAllocator())), + _pendingLoads(decltype(_pendingLoads)::allocator_type(TR::Compiler->persistentAllocator())) { } +bool +TR_AOTDependencyTable::trackMethod(J9VMThread *vmThread, J9Method *method, J9ROMMethod *romMethod, bool &dependenciesSatisfied) + { + const uintptr_t *methodDependencies = NULL; + if (!_sharedCache->methodHasAOTBodyWithDependencies(vmThread, romMethod, methodDependencies)) + return false; + + if (!methodDependencies) + { + dependenciesSatisfied = true; + return true; + } + + OMR::CriticalSection cs(_tableMonitor); + if (!isActive()) + return false; + + try + { + uintptr_t totalDependencies = *methodDependencies; + uintptr_t numberRemainingDependencies = totalDependencies; + + auto m_it = _methodMap.insert({method, {0, methodDependencies}}); + auto methodEntry = &(*m_it.first); + + for (size_t i = 1; i <= totalDependencies; ++i) + { + bool needsInitialization = false; + uintptr_t chainOffset = decodeDependencyOffset(methodDependencies[i], needsInitialization); + uintptr_t offset = _sharedCache->startingROMClassOffsetOfClassChain(_sharedCache->pointerFromOffsetInSharedCache(chainOffset)); + auto entry = getOffsetEntry(offset, true); + if (needsInitialization) + entry->_waitingInitMethods.insert(methodEntry); + else + entry->_waitingLoadMethods.insert(methodEntry); + + if (findCandidateForDependency(entry->_loadedClasses, needsInitialization)) + numberRemainingDependencies -= 1; + } + + if (numberRemainingDependencies == 0) + { + stopTracking(methodEntry); + dependenciesSatisfied = true; + } + else + { + methodEntry->second._remainingDependencies = numberRemainingDependencies; + } + } + catch (std::exception&) + { + deactivateTable(); + return false; + } + + return true; + } + +void +TR_AOTDependencyTable::methodWillBeCompiled(J9Method *method) + { + OMR::CriticalSection cs(_tableMonitor); + if (!isActive()) + return; + + // TODO: For now we simply stop tracking method if for some reason a + // compilation is triggered for it, but if the compilation in question is an + // AOT load we might consider preventing the load from taking place (by + // increasing the counts and continuing to track the method, or marking the + // method as ineligible for loads and giving up on tracking it). + stopTracking(method); + } + +void +TR_AOTDependencyTable::stopTracking(MethodEntryRef entry) + { + auto methodEntry = entry->second; + auto dependencyChain = methodEntry._dependencyChain; + auto dependencyChainLength = *dependencyChain; + + for (size_t i = 1; i <= dependencyChainLength; ++i) + { + bool needsInitialization = false; + uintptr_t chainOffset = decodeDependencyOffset(dependencyChain[i], needsInitialization); + uintptr_t offset = _sharedCache->startingROMClassOffsetOfClassChain(_sharedCache->pointerFromOffsetInSharedCache(chainOffset)); + + auto o_it = _offsetMap.find(offset); + + if (needsInitialization) + o_it->second._waitingInitMethods.erase(entry); + else + o_it->second._waitingLoadMethods.erase(entry); + + eraseOffsetEntryIfEmpty(&o_it->second, offset); + } + + _methodMap.erase(entry->first); + } + +void +TR_AOTDependencyTable::stopTracking(J9Method *method) + { + auto entry = _methodMap.find(method); + if (entry != _methodMap.end()) + stopTracking(&*entry); + } + +void +TR_AOTDependencyTable::eraseOffsetEntryIfEmpty(OffsetEntry *entry, uintptr_t offset) + { + if (entry->_loadedClasses.empty() && entry->_waitingInitMethods.empty() && entry->_waitingLoadMethods.empty()) + _offsetMap.erase(offset); + } + void TR_AOTDependencyTable::classLoadEvent(TR_OpaqueClassBlock *clazz, bool isClassLoad, bool isClassInitialization) { @@ -60,17 +178,42 @@ TR_AOTDependencyTable::classLoadEvent(TR_OpaqueClassBlock *clazz, bool isClassLo catch (std::exception&) { deactivateTable(); + return; + } + + resolvePendingLoads(); + } + +void +TR_AOTDependencyTable::checkForSatisfaction(PersistentUnorderedSet waitingMethods) + { + for (auto &entry: waitingMethods) + { + if (entry->second._remainingDependencies) + _pendingLoads.insert(entry); + else + --entry->second._remainingDependencies; } } void TR_AOTDependencyTable::classLoadEventAtOffset(J9Class *ramClass, uintptr_t offset, bool isClassLoad, bool isClassInitialization) { - auto entry = getOffsetEntry(offset, isClassLoad); - TR_ASSERT(entry || !isClassLoad, "Class %p offset %lu initialized without loading"); + auto offsetEntry = getOffsetEntry(offset, isClassLoad); + TR_ASSERT(offsetEntry || !isClassLoad, "Class %p offset %lu initialized without loading", ramClass, offset); + + // Check for dependency satisfaction if this is the first class initialized + // for this offset + if (isClassInitialization && (NULL != findCandidateForDependency(offsetEntry->_loadedClasses, true))) + checkForSatisfaction(offsetEntry->_waitingInitMethods); + // Track the class, and also check for dependency satisfaction if this is the + // first class loaded for this offset if (isClassLoad) - entry->_loadedClasses.insert(ramClass); + { + checkForSatisfaction(offsetEntry->_waitingLoadMethods); + offsetEntry->_loadedClasses.insert(ramClass); + } } OffsetEntry * @@ -83,7 +226,9 @@ TR_AOTDependencyTable::getOffsetEntry(uintptr_t offset, bool create) if (create) { PersistentUnorderedSet loadedClasses(PersistentUnorderedSet::allocator_type(TR::Compiler->persistentAllocator())); - return &(*_offsetMap.insert(it, {offset, {loadedClasses}})).second; + PersistentUnorderedSet waitingLoadMethods(PersistentUnorderedSet::allocator_type(TR::Compiler->persistentAllocator())); + PersistentUnorderedSet waitingInitMethods(PersistentUnorderedSet::allocator_type(TR::Compiler->persistentAllocator())); + return &(*_offsetMap.insert(it, {offset, {loadedClasses, waitingLoadMethods, waitingInitMethods}})).second; } return NULL; @@ -101,7 +246,19 @@ TR_AOTDependencyTable::invalidateUnloadedClass(TR_OpaqueClassBlock *clazz) if (!isActive()) return; - invalidateClassAtOffset((J9Class *)clazz, classOffset); + auto ramClass = (J9Class *)clazz; + if (invalidateClassAtOffset(ramClass, classOffset)) + invalidateMethodsOfClass(ramClass); + } + +void +TR_AOTDependencyTable::registerDissatisfaction(PersistentUnorderedSet waitingMethods) + { + for (auto& entry: waitingMethods) + { + ++entry->second._remainingDependencies; + _pendingLoads.erase(entry); + } } bool @@ -111,13 +268,31 @@ TR_AOTDependencyTable::invalidateClassAtOffset(J9Class *ramClass, uintptr_t romC if (entry) { entry->_loadedClasses.erase(ramClass); + if (entry->_loadedClasses.empty()) - _offsetMap.erase(romClassOffset); + { + registerDissatisfaction(entry->_waitingLoadMethods); + registerDissatisfaction(entry->_waitingInitMethods); + eraseOffsetEntryIfEmpty(entry, romClassOffset); + } + else if (!findCandidateForDependency(entry->_loadedClasses, true)) + { + registerDissatisfaction(entry->_waitingInitMethods); + } + return true; } + return false; } +void +TR_AOTDependencyTable::invalidateMethodsOfClass(J9Class *ramClass) + { + for (uint32_t i = 0; i < ramClass->romClass->romMethodCount; i++) + stopTracking(&ramClass->ramMethods[i]); + } + // If an entry exists for a class, remove it. Otherwise, if we should // revalidate, add an entry if the class has a valid chain. void @@ -159,6 +334,7 @@ TR_AOTDependencyTable::invalidateRedefinedClass(TR_PersistentCHTable *table, TR_ // old and fresh class pointers. if (invalidateClassAtOffset((J9Class *)oldClass, oldClassOffset)) { + invalidateMethodsOfClass((J9Class *)oldClass); auto freshRamClass = (J9Class *)freshClass; bool initialized = J9ClassInitSucceeded == freshRamClass->initializeStatus; classLoadEventAtOffset(freshRamClass, freshClassOffset, true, initialized); @@ -167,8 +343,10 @@ TR_AOTDependencyTable::invalidateRedefinedClass(TR_PersistentCHTable *table, TR_ catch (std::exception&) { deactivateTable(); + return; } + resolvePendingLoads(); return; } @@ -186,6 +364,9 @@ TR_AOTDependencyTable::invalidateRedefinedClass(TR_PersistentCHTable *table, TR_ try { + // Invalidate the methods of oldClass first, so _pendingLoads doesn't have + // to be cleared of invalidated method entries + invalidateMethodsOfClass((J9Class *)oldClass); for (auto iter = classList.begin(); iter != classList.end(); iter++) { auto clazz = (J9Class *)(*iter)->getClassId(); @@ -199,6 +380,22 @@ TR_AOTDependencyTable::invalidateRedefinedClass(TR_PersistentCHTable *table, TR_ { deactivateTable(); } + + resolvePendingLoads(); + } + +void +TR_AOTDependencyTable::resolvePendingLoads() + { + for (auto& entry: _pendingLoads) + { + auto method = entry->first; + auto count = TR::CompilationInfo::getInvocationCount(method); + while ((count > 0) && !TR::CompilationInfo::setInvocationCount(method, count, 0)) + count = TR::CompilationInfo::getInvocationCount(method); + stopTracking(entry); + } + _pendingLoads.clear(); } TR_OpaqueClassBlock * @@ -213,10 +410,16 @@ TR_AOTDependencyTable::findClassCandidate(uintptr_t romClassOffset) if (it == _offsetMap.end()) return NULL; - for (const auto& clazz: it->second._loadedClasses) + return (TR_OpaqueClassBlock *)findCandidateForDependency(it->second._loadedClasses, true); + } + +J9Class * +TR_AOTDependencyTable::findCandidateForDependency(PersistentUnorderedSet &loadedClasses, bool needsInitialization) + { + for (const auto& clazz: loadedClasses) { - if (J9ClassInitSucceeded == clazz->initializeStatus) - return (TR_OpaqueClassBlock *)clazz; + if (!needsInitialization || (J9ClassInitSucceeded == clazz->initializeStatus)) + return clazz; } return NULL; @@ -226,6 +429,8 @@ void TR_AOTDependencyTable::deactivateTable() { _offsetMap.clear(); + _methodMap.clear(); + _pendingLoads.clear(); setInactive(); } diff --git a/runtime/compiler/env/DependencyTable.hpp b/runtime/compiler/env/DependencyTable.hpp index 40dd5921354..6ee170cc2f0 100644 --- a/runtime/compiler/env/DependencyTable.hpp +++ b/runtime/compiler/env/DependencyTable.hpp @@ -34,6 +34,8 @@ class TR_J9SharedCache; class TR_AOTDependencyTable { public: + bool trackMethod(J9VMThread *vmThread, J9Method *method, J9ROMMethod *romMethod, bool &dependenciesSatisfied) { return false; } + void methodIsBeingCompiled(J9Method *method) {} void classLoadEvent(TR_OpaqueClassBlock *ramClass, bool isClassLoad, bool isClassInitialization) {} void invalidateUnloadedClass(TR_OpaqueClassBlock *ramClass) {} void invalidateRedefinedClass(TR_PersistentCHTable *table, TR_J9VMBase *fej9, TR_OpaqueClassBlock *oldClass, TR_OpaqueClassBlock *freshClass) {} @@ -42,9 +44,26 @@ class TR_AOTDependencyTable #else +struct MethodEntry + { + // The number of dependencies left to be satisfied + uintptr_t _remainingDependencies; + // The dependency chain stored in the local SCC (used to stop tracking a + // method) + const uintptr_t *_dependencyChain; + }; + +typedef std::pair* MethodEntryRef; + struct OffsetEntry { + // Classes that have been loaded and have a particular valid class chain + // offset PersistentUnorderedSet _loadedClasses; + // Methods waiting for a class with this chain offset to be loaded + PersistentUnorderedSet _waitingLoadMethods; + // Methods waiting for a class with this chain offset to be initialized + PersistentUnorderedSet _waitingInitMethods; }; /** @@ -70,34 +89,101 @@ class TR_AOTDependencyTable public: TR_PERSISTENT_ALLOC(TR_Memory::PersistentCHTable) TR_AOTDependencyTable(TR_J9SharedCache *sharedCache); - // Update the table in response to a class load or initialization event. The - // isClassInitialization parameter is currently unused. + + // If the given method has an AOT body with stored dependencies in the local + // SCC, trackMethod() will determine how many are currently satisfied. If all + // are, dependenciesSatisfied will be set to true. Otherwise, the method and + // its dependencies will be tracked until all the dependencies are satisfied, + // at which point the method's count will be reduced to zero. + // + // Returns true if the method had stored dependencies and either they were + // all satisfied immediately, or the method was successfully tracked. + bool trackMethod(J9VMThread *vmThread, J9Method *method, J9ROMMethod *romMethod, bool &dependenciesSatisfied); + + // Inform the dependency table that a method is being compiled, so it can + // stop tracking the method. Will invalidate the MethodEntryRef pointer for + // method, if it was being tracked. + void methodWillBeCompiled(J9Method *method); + + // Update the table in response to a class load or initialization event. void classLoadEvent(TR_OpaqueClassBlock *ramClass, bool isClassLoad, bool isClassInitialization); - // Invalidate an unloaded class + // Invalidate an unloaded class. Will invalidate the MethodEntryRef for every + // RAM method of ramClass. void invalidateUnloadedClass(TR_OpaqueClassBlock *ramClass); - // Invalidate a redefined class + // Invalidate a redefined class. Will invalidate the MethodEntryRef for every + // RAM method of ramClass. void invalidateRedefinedClass(TR_PersistentCHTable *table, TR_J9VMBase *fej9, TR_OpaqueClassBlock *oldClass, TR_OpaqueClassBlock *freshClass); // Given a ROM class offset, return an initialized class with a valid class // chain starting with that offset. TR_OpaqueClassBlock *findClassCandidate(uintptr_t offset); + static uintptr_t decodeDependencyOffset(uintptr_t offset) + { + return offset | 1; + } + static uintptr_t decodeDependencyOffset(uintptr_t offset, bool &needsInitialization) + { + needsInitialization = (offset & 1) == 1; + return decodeDependencyOffset(offset); + } + static uintptr_t encodeDependencyOffset(uintptr_t offset, bool needsInitialization) + { + return needsInitialization ? offset : (offset & ~1); + } + private: bool isActive() const { return _isActive; } void setInactive() { _isActive = false; } + // Deallocate the internal structures of the table and mark the table as // inactive. Must be called with the _tableMonitor in hand. void deactivateTable(); + // Register a class load event for ramClass at offset. If any methods had + // their dependencies satisfied by this event, they will be added to + // methodsToQueue. void classLoadEventAtOffset(J9Class *ramClass, uintptr_t offset, bool isClassLoad, bool isClassInitialization); + // Invalidate a class with a particular ROM class offset. Returns false if - // the class wasn't tracked. + // the class wasn't tracked. If pendingMethodQueue is not NULL, we will also + // remove all methods whose dependencies became unsatisfied from + // pendingMethodQueue. bool invalidateClassAtOffset(J9Class *ramClass, uintptr_t romClassOffset); + + // Invalidate any tracked methods of ramClass. This will invalidate the + // MethodEntryRef of those methods. When registering a class redefinition + // event, this is best called before any revalidation occurs, so that stale + // method entry pointers are not collected in _pendingLoads. + void invalidateMethodsOfClass(J9Class *ramClass); + void recheckSubclass(J9Class *ramClass, uintptr_t offset, bool shouldRevalidate); + + // Get the OffsetEntry corresponding to offset. If create is true this will + // never return a NULL entry (but it may throw). OffsetEntry *getOffsetEntry(uintptr_t offset, bool create); + J9Class *findCandidateForDependency(PersistentUnorderedSet &loadedClasses, bool needsInitialization); + + // Stop tracking the given method. This will invalidate the MethodEntryRef + // for the method. + void stopTracking(MethodEntryRef entry); + void stopTracking(J9Method *method); + + // Queue and clear the _pendingLoads, and remove those methods from tracking. + // Must be called at the end of any dependency table operation that could + // have led to a pending load being registered. + void resolvePendingLoads(); + + // Erase the given entry at offset if the entry is empty. This will + // invalidate the pointer to OffsetEntry. + void eraseOffsetEntryIfEmpty(OffsetEntry *entry, uintptr_t offset); + + void checkForSatisfaction(PersistentUnorderedSet waitingMethods); + void registerDissatisfaction(PersistentUnorderedSet waitingMethods); + // Initially true, and set to false if there is a failure to allocate. bool _isActive; @@ -111,6 +197,13 @@ class TR_AOTDependencyTable // works because there can be at most one class chain in the SCC whose entry // is a particular ROM class offset. PersistentUnorderedMap _offsetMap; + + // A map from methods to their tracking information in the dependency table + PersistentUnorderedMap _methodMap; + + // Any pending method loads that were triggered by the current dependency + // table transaction + PersistentUnorderedSet _pendingLoads; }; #endif /* defined(PERSISTENT_COLLECTIONS_UNSUPPORTED) */ diff --git a/runtime/compiler/env/J9SharedCache.cpp b/runtime/compiler/env/J9SharedCache.cpp index b68cc812269..7d3c67c3eb1 100644 --- a/runtime/compiler/env/J9SharedCache.cpp +++ b/runtime/compiler/env/J9SharedCache.cpp @@ -986,11 +986,16 @@ TR_J9SharedCache::isPtrToROMClassesSectionInSharedCache(void *ptr, uintptr_t *ca J9ROMClass * TR_J9SharedCache::startingROMClassOfClassChain(UDATA *classChain) { - UDATA lengthInBytes = classChain[0]; - TR_ASSERT_FATAL(lengthInBytes >= 2 * sizeof (UDATA), "class chain is too short!"); + return romClassFromOffsetInSharedCache(startingROMClassOffsetOfClassChain(classChain)); + } - UDATA romClassOffset = classChain[1]; - return romClassFromOffsetInSharedCache(romClassOffset); +uintptr_t +TR_J9SharedCache::startingROMClassOffsetOfClassChain(void *chain) + { + auto classChain = (uintptr_t *)chain; + uintptr_t lengthInBytes = classChain[0]; + TR_ASSERT_FATAL(lengthInBytes >= 2 * sizeof (UDATA), "class chain is too short!"); + return classChain[1]; } // convert an offset into a string of 8 characters diff --git a/runtime/compiler/env/J9SharedCache.hpp b/runtime/compiler/env/J9SharedCache.hpp index ebfd6d4f56a..0a56952d579 100644 --- a/runtime/compiler/env/J9SharedCache.hpp +++ b/runtime/compiler/env/J9SharedCache.hpp @@ -368,6 +368,7 @@ class TR_J9SharedCache : public TR_SharedCache virtual bool isOffsetOfPtrToROMClassesSectionInSharedCache(uintptr_t offset, void **ptr = NULL); J9ROMClass *startingROMClassOfClassChain(UDATA *classChain); + uintptr_t startingROMClassOffsetOfClassChain(void *chain); virtual uintptr_t getClassChainOffsetIdentifyingLoader(TR_OpaqueClassBlock *clazz, uintptr_t **classChain = NULL); @@ -421,11 +422,14 @@ class TR_J9SharedCache : public TR_SharedCache /** * \brief Store the dependencies of an AOT method in the SCC * - * The dependencies of an AOT method are encoded as an array of uintptr_t values. The first entry is the number - * of elements in the entire array. The subsequent entries are encoded offsets to the class chains of classes that - * need to be loaded or initialized before the compiled body can be loaded. If the class must be initialized, the - * entry will be the offset itself (which necessarily has a set low bit), and if the class only needs to be loaded - * it will be the offset with the low bit cleared. * + * The dependencies of an AOT method are encoded as an array of uintptr_t + * values. The first entry is the number of entries after the first, the + * number of dependencies in the array. The subsequent entries are encoded + * offsets to the class chains of classes that need to be loaded or + * initialized before the compiled body can be loaded. If the class must be + * initialized, the entry will be the offset itself (which necessarily has a + * set low bit), and if the class only needs to be loaded it will be the + * offset with the low bit cleared. * * \param[in] vmThread VM thread * \param[in] methodDependencies The dependencies of the AOT compilation