From 01e16111ddb35d99dfd1f39cdccb7d8f36e02372 Mon Sep 17 00:00:00 2001 From: Devin Papineau Date: Fri, 24 Jan 2025 15:20:31 -0500 Subject: [PATCH] Track JIT body invalidation reasons as bit flags ...and specify the reason whenever we invalidate a JIT body. Post-restore exclusion was already specially distinguished by a flag in TR_PersistentMethodInfo. Preexistence invalidation should also have been similarly distinguished, but the logic to avoid making new preexistence assumptions has been treating all invalidations as potentially preexistence-related. This change is in preparation to add a new invalidation reason: the unloading of an inlined method. It will be important to know when a method has had a JIT body invalidated due to unloading so that when recompiling, inlining can be restricted to avoid repeated invalidation. --- runtime/compiler/control/HookedByTheJit.cpp | 4 +- .../control/J9CompilationStrategy.cpp | 2 +- .../control/J9CompilationStrategy.hpp | 1 + runtime/compiler/control/J9Recompilation.cpp | 5 +- runtime/compiler/control/J9Recompilation.hpp | 2 +- .../compiler/control/OptionsPostRestore.cpp | 5 +- .../compiler/control/RecompilationInfo.hpp | 67 +++++++++++++++---- runtime/compiler/control/rossa.cpp | 2 +- runtime/compiler/runtime/JitRuntime.cpp | 10 +-- .../compiler/runtime/RuntimeAssumptions.cpp | 3 +- 10 files changed, 72 insertions(+), 29 deletions(-) diff --git a/runtime/compiler/control/HookedByTheJit.cpp b/runtime/compiler/control/HookedByTheJit.cpp index abedb56643e..60dedbc3bc4 100644 --- a/runtime/compiler/control/HookedByTheJit.cpp +++ b/runtime/compiler/control/HookedByTheJit.cpp @@ -2595,7 +2595,9 @@ void jitClassesRedefined(J9VMThread * currentThread, UDATA classCount, J9JITRede if (bodyInfo) { reportHookDetail(currentThread, "jitClassesRedefined", " Invalidate method body stale=%p startPC=%p", staleMethod, startPC); - TR::Recompilation::invalidateMethodBody(startPC, fe); + TR::Recompilation::invalidateMethodBody( + startPC, fe, TR_JitBodyInvalidations::HCR); + bodyInfo->setDisableSampling(true); TR_PersistentMethodInfo *pmi = bodyInfo->getMethodInfo(); if (pmi) diff --git a/runtime/compiler/control/J9CompilationStrategy.cpp b/runtime/compiler/control/J9CompilationStrategy.cpp index f972a3aed89..4a14e6ea4a9 100644 --- a/runtime/compiler/control/J9CompilationStrategy.cpp +++ b/runtime/compiler/control/J9CompilationStrategy.cpp @@ -180,7 +180,7 @@ TR_OptimizationPlan *J9::CompilationStrategy::processEvent(TR_MethodEvent *event hotnessLevel = bodyInfo->getHotness(); plan = TR_OptimizationPlan::alloc(hotnessLevel); *newPlanCreated = true; - bodyInfo->getMethodInfo()->incrementNumberOfInvalidations(); + bodyInfo->getMethodInfo()->addInvalidationReasons(bodyInfo->invalidationReasons()); // the following is just for compatibility with older implementation //bodyInfo->getMethodInfo()->setNextCompileLevel(hotnessLevel, false); // no profiling diff --git a/runtime/compiler/control/J9CompilationStrategy.hpp b/runtime/compiler/control/J9CompilationStrategy.hpp index 444aaf08ec7..83ccb90181f 100644 --- a/runtime/compiler/control/J9CompilationStrategy.hpp +++ b/runtime/compiler/control/J9CompilationStrategy.hpp @@ -33,6 +33,7 @@ #endif #include "control/OMRCompilationStrategy.hpp" +#include "control/RecompilationInfo.hpp" #include "compile/CompilationTypes.hpp" #include "runtime/CodeCacheManager.hpp" diff --git a/runtime/compiler/control/J9Recompilation.cpp b/runtime/compiler/control/J9Recompilation.cpp index 7fbe290dfa7..fcd6ccaae40 100644 --- a/runtime/compiler/control/J9Recompilation.cpp +++ b/runtime/compiler/control/J9Recompilation.cpp @@ -588,7 +588,7 @@ TR_PersistentMethodInfo::TR_PersistentMethodInfo(TR::Compilation *comp) : _bestProfileInfo(0), _optimizationPlan(0), _catchBlockCounter(0), - _numberOfInvalidations(0), + _numberOfPreexistenceInvalidations(0), _numberOfInlinedMethodRedefinition(0), _numPrexAssumptions(0) { @@ -619,7 +619,7 @@ TR_PersistentMethodInfo::TR_PersistentMethodInfo(TR_OpaqueMethodBlock *methodInf _bestProfileInfo(0), _optimizationPlan(0), _catchBlockCounter(0), - _numberOfInvalidations(0), + _numberOfPreexistenceInvalidations(0), _numberOfInlinedMethodRedefinition(0), _numPrexAssumptions(0) { @@ -654,7 +654,6 @@ TR_PersistentJittedBodyInfo::TR_PersistentJittedBodyInfo( _flags(0), _sampleIntervalCount(0), _startCount(0), - _isInvalidated(false), _aggressiveRecompilationChances((uint8_t)TR::Options::_aggressiveRecompilationChances), _startPCAfterPreviousCompile(0), _longRunningInterpreted(false), diff --git a/runtime/compiler/control/J9Recompilation.hpp b/runtime/compiler/control/J9Recompilation.hpp index 4970d21f9a7..5464f3f8694 100644 --- a/runtime/compiler/control/J9Recompilation.hpp +++ b/runtime/compiler/control/J9Recompilation.hpp @@ -118,7 +118,7 @@ class Recompilation : public OMR::RecompilationConnector static bool isAlreadyBeingCompiled(TR_OpaqueMethodBlock *methodInfo, void *startPC, TR_FrontEnd *fe); static void methodCannotBeRecompiled(void *oldStartPC, TR_FrontEnd *fe); - static void invalidateMethodBody(void *startPC, TR_FrontEnd *fe); + static void invalidateMethodBody(void *startPC, TR_FrontEnd *fe, TR_JitBodyInvalidations::Reason reason); // Called at runtime to sample a method // diff --git a/runtime/compiler/control/OptionsPostRestore.cpp b/runtime/compiler/control/OptionsPostRestore.cpp index f31c8830537..0bb9e51760f 100644 --- a/runtime/compiler/control/OptionsPostRestore.cpp +++ b/runtime/compiler/control/OptionsPostRestore.cpp @@ -437,9 +437,8 @@ J9::OptionsPostRestore::invalidateCompiledMethod(J9Method *method, TR_J9VMBase * } TR_PersistentMethodInfo *pmi = bodyInfo->getMethodInfo(); - pmi->setIsExcludedPostRestore(); - - TR::Recompilation::invalidateMethodBody(startPC, fej9); + TR::Recompilation::invalidateMethodBody( + startPC, fej9, TR_JitBodyInvalidations::PostRestoreExclude); // TODO: add method to a list to check the stack of java threads to print out message } diff --git a/runtime/compiler/control/RecompilationInfo.hpp b/runtime/compiler/control/RecompilationInfo.hpp index cdf9a0965c6..0e7ba59ebdd 100644 --- a/runtime/compiler/control/RecompilationInfo.hpp +++ b/runtime/compiler/control/RecompilationInfo.hpp @@ -70,6 +70,25 @@ static int32_t profilingFreqTable [] = { 19, 29, 47, 47, 47, 53 }; / namespace OMR { class Recompilation; } namespace J9 { class Recompilation; } +class TR_JitBodyInvalidations + { + public: + enum Reason + { + HCR, // outermost method has been obsoleted by HCR + Preexistence, // preexistence assumption has been invalidated + PostRestoreExclude, // method is excluded post-restore, should be interpreted + }; + + bool isEmpty() const { return _flags.getValue() == 0; } + bool contains(Reason reason) const { return _flags.testAny(1 << reason); } + void add(Reason reason) { _flags.set(1 << reason); } + void add(TR_JitBodyInvalidations reasons) { _flags.set(reasons._flags.getValue()); } + + private: + flags8_t _flags; + }; + // Persistent information associated with a method for recompilation // class TR_PersistentMethodInfo @@ -157,8 +176,16 @@ class TR_PersistentMethodInfo bool doesntKillAnything() { return _flags.testAll(RefinedAliasesMask); } - bool isExcludedPostRestore() { return _flags.testAny(IsExcludedPostRestore); } - void setIsExcludedPostRestore(bool b = true) { _flags.set(IsExcludedPostRestore, b); } + bool isExcludedPostRestore() + { + return _invalidationReasons.contains(TR_JitBodyInvalidations::PostRestoreExclude); + } + + /** + * \brief Get the set of all reasons for prior invalidations of JIT bodies + * belonging to this method. + */ + TR_JitBodyInvalidations invalidationReasons() { return _invalidationReasons; } uint16_t getTimeStamp() { return _timeStamp; } @@ -167,13 +194,21 @@ class TR_PersistentMethodInfo uint32_t getCatchBlockCounter() const { return _catchBlockCounter; } uint32_t *getCatchBlockCounterAddress() { return &_catchBlockCounter; } void incrementCatchBlockCounter() { _catchBlockCounter++; } - uint8_t getNumberOfInvalidations() {return _numberOfInvalidations;} - void incrementNumberOfInvalidations() {_numberOfInvalidations++;} + uint8_t getNumberOfPreexistenceInvalidations() {return _numberOfPreexistenceInvalidations;} uint8_t getNumberOfInlinedMethodRedefinition() {return _numberOfInlinedMethodRedefinition;} void incrementNumberOfInlinedMethodRedefinition() {_numberOfInlinedMethodRedefinition++;} int16_t getNumPrexAssumptions() {return _numPrexAssumptions;} void incNumPrexAssumptions() {_numPrexAssumptions++;} + void addInvalidationReasons(TR_JitBodyInvalidations reasons) + { + _invalidationReasons.add(reasons); + if (reasons.contains(TR_JitBodyInvalidations::Preexistence)) + { + _numberOfPreexistenceInvalidations++; + } + } + enum InfoBits { // Normally set by the previous compilation to indicate that the next @@ -245,10 +280,6 @@ class TR_PersistentMethodInfo WasScannedForInlining = 0x00400000, // New scanning for warm method inlining IsInDataCache = 0x00800000, // This TR_PersistentMethodInfo is stored in the datacache for AOT - IsExcludedPostRestore = 0x01000000, // Post-restore, if a method should be excluded, this bit will allow - // J9::Recompilation::methodCannotBeRecompiled to patch the startPC - // to call the interpreter - lastFlag = 0x80000000 }; @@ -295,9 +326,10 @@ class TR_PersistentMethodInfo TR_OptimizationPlan *_optimizationPlan; uint32_t _catchBlockCounter; // how many times a catch block was executed uint16_t _timeStamp; - uint8_t _numberOfInvalidations; // how many times this method has been invalidated + uint8_t _numberOfPreexistenceInvalidations; // how many times this method has been invalidated due to preexistence uint8_t _numberOfInlinedMethodRedefinition; // how many times this method triggers recompilation because of its inlined callees being redefined int16_t _numPrexAssumptions; + TR_JitBodyInvalidations _invalidationReasons; TR_PersistentProfileInfo *_bestProfileInfo; TR_PersistentProfileInfo *_recentProfileInfo; @@ -306,7 +338,6 @@ class TR_PersistentMethodInfo void setForSharedInfo(TR_PersistentProfileInfo** ptr, TR_PersistentProfileInfo *newInfo); }; - // This information is kept for every jitted method that can be recompiled // It may be garbage collected along with the jitted method // The only way to get the following information is via a pointer that is kept @@ -346,8 +377,18 @@ class TR_PersistentJittedBodyInfo void setSamplingRecomp() { _flags.set(SamplingRecomp, true); } bool getIsPushedForRecompilation(){ return _flags.testAny(IsPushedForRecompilation); } void setIsPushedForRecompilation(){ _flags.set(IsPushedForRecompilation, true); } - bool getIsInvalidated() { return _isInvalidated; } - void setIsInvalidated() { _isInvalidated = true; } + + /** + * \brief Get the set of all reasons (there may be multiple) for which this + * JIT body has been invalidated. + */ + TR_JitBodyInvalidations invalidationReasons() { return _invalidationReasons; } + + bool getIsInvalidated() { return !_invalidationReasons.isEmpty(); } + void setIsInvalidated(TR_JitBodyInvalidations::Reason reason) + { + _invalidationReasons.add(reason); + } bool getFastHotRecompilation() { return _flags.testAny(FastHotRecompilation); } void setFastHotRecompilation(bool b){ _flags.set(FastHotRecompilation, b); } @@ -472,7 +513,7 @@ class TR_PersistentJittedBodyInfo uint8_t _aggressiveRecompilationChances; TR_Hotness _hotness; uint8_t _numScorchingIntervals; // How many times we reached scorching recompilation decision points - bool _isInvalidated; + TR_JitBodyInvalidations _invalidationReasons; bool _longRunningInterpreted; // This cannot be moved into _flags due to synchronization issues TR_PersistentProfileInfo * _profileInfo; public: diff --git a/runtime/compiler/control/rossa.cpp b/runtime/compiler/control/rossa.cpp index 621a09ed720..43fc1e9c2bf 100644 --- a/runtime/compiler/control/rossa.cpp +++ b/runtime/compiler/control/rossa.cpp @@ -305,7 +305,7 @@ j9jit_testarossa_err( // Obsolete method bodies are invalid. // TR::Recompilation::fixUpMethodCode(oldStartPC); - jbi->setIsInvalidated(); + jbi->setIsInvalidated(TR_JitBodyInvalidations::HCR); } if (jbi->getIsInvalidated()) diff --git a/runtime/compiler/runtime/JitRuntime.cpp b/runtime/compiler/runtime/JitRuntime.cpp index d1238136621..8631447ad14 100644 --- a/runtime/compiler/runtime/JitRuntime.cpp +++ b/runtime/compiler/runtime/JitRuntime.cpp @@ -237,14 +237,14 @@ J9::Recompilation::sampleMethod( } } -void J9::Recompilation::invalidateMethodBody(void *startPC, TR_FrontEnd *fe) +void J9::Recompilation::invalidateMethodBody( + void *startPC, TR_FrontEnd *fe, TR_JitBodyInvalidations::Reason reason) { - // Pre-existence assumptions for this method have been violated. Make the - // method no-longer runnable and schedule it for sync recompilation - // + // Make the method no longer runnable and schedule it for sync recompilation + // or switch to interpreter J9::PrivateLinkage::LinkageInfo *linkageInfo = J9::PrivateLinkage::LinkageInfo::get(startPC); TR_PersistentJittedBodyInfo *bodyInfo = getJittedBodyInfoFromPC(startPC); - bodyInfo->setIsInvalidated(); // bodyInfo must exist + bodyInfo->setIsInvalidated(reason); // bodyInfo must exist // If the compilation has been attempted before then we are fine (in case of success, // each caller is being re-directed to the new method -- in case if failure, all callers diff --git a/runtime/compiler/runtime/RuntimeAssumptions.cpp b/runtime/compiler/runtime/RuntimeAssumptions.cpp index 05047ca1911..9345c542058 100644 --- a/runtime/compiler/runtime/RuntimeAssumptions.cpp +++ b/runtime/compiler/runtime/RuntimeAssumptions.cpp @@ -129,7 +129,8 @@ TR_PreXRecompile::compensate(TR_FrontEnd *fe, bool, void *) TR_J9VMBase *fej9 = (TR_J9VMBase *)fe; #if (defined(TR_HOST_X86) || defined(TR_HOST_POWER) || defined(TR_HOST_S390) || defined(TR_HOST_ARM) || defined(TR_HOST_ARM64)) - TR::Recompilation::invalidateMethodBody(_startPC, fe); + TR::Recompilation::invalidateMethodBody( + _startPC, fe, TR_JitBodyInvalidations::Preexistence); // Generate a trace point fej9->reportPrexInvalidation(_startPC); #else