Add background type preloading based on multicorejit (#52595)
authorGleb Balykov <g.balykov@samsung.com>
Fri, 4 Jun 2021 21:04:59 +0000 (00:04 +0300)
committerGitHub <noreply@github.com>
Fri, 4 Jun 2021 21:04:59 +0000 (14:04 -0700)
* Add background type preloading based on multicorejit

This is a second part of #48326 change, which enables handling of methods loaded from r2r images. Background thread of multicorejit now not only jits methods but also loads methods from R2R images. This allows to load types in background thread.

This is required as part of https://github.com/dotnet/runtime/issues/45748 change (specifically, https://github.com/dotnet/runtime/issues/45748#issuecomment-750889697), goal of which is to enable background type preloading using multicorejit.

src/coreclr/vm/method.hpp
src/coreclr/vm/multicorejit.cpp
src/coreclr/vm/multicorejit.h
src/coreclr/vm/multicorejitimpl.h
src/coreclr/vm/multicorejitplayer.cpp
src/coreclr/vm/prestub.cpp

index 491eeb6..c929cbe 100644 (file)
@@ -2023,7 +2023,7 @@ private:
     PCODE GetPrecompiledCode(PrepareCodeConfig* pConfig, bool shouldTier);
     PCODE GetPrecompiledNgenCode(PrepareCodeConfig* pConfig);
     PCODE GetPrecompiledR2RCode(PrepareCodeConfig* pConfig);
-    PCODE GetMulticoreJitCode(PrepareCodeConfig* pConfig, bool* pWasTier0Jit);
+    PCODE GetMulticoreJitCode(PrepareCodeConfig* pConfig, bool* pWasTier0);
     COR_ILMETHOD_DECODER* GetAndVerifyILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory);
     COR_ILMETHOD_DECODER* GetAndVerifyMetadataILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory);
     COR_ILMETHOD_DECODER* GetAndVerifyNoMetadataILHeader();
@@ -2208,7 +2208,8 @@ public:
         }
     }
 
-    bool FinalizeOptimizationTierForTier0Jit();
+    bool FinalizeOptimizationTierForTier0Load();
+    bool FinalizeOptimizationTierForTier0LoadOrJit();
 #endif
 
 public:
@@ -2300,21 +2301,21 @@ public:
 class MulticoreJitPrepareCodeConfig : public PrepareCodeConfig
 {
 private:
-    bool m_wasTier0Jit;
+    bool m_wasTier0;
 
 public:
     MulticoreJitPrepareCodeConfig(MethodDesc* pMethod);
 
-    bool WasTier0Jit() const
+    bool WasTier0() const
     {
         LIMITED_METHOD_CONTRACT;
-        return m_wasTier0Jit;
+        return m_wasTier0;
     }
 
-    void SetWasTier0Jit()
+    void SetWasTier0()
     {
         LIMITED_METHOD_CONTRACT;
-        m_wasTier0Jit = true;
+        m_wasTier0 = true;
     }
 
     virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse) override;
index 1feb731..ae8582f 100644 (file)
@@ -869,7 +869,7 @@ void MulticoreJitRecorder::PreRecordFirstMethod()
 }
 
 
-void MulticoreJitRecorder::RecordMethodJit(MethodDesc * pMethod, bool application)
+void MulticoreJitRecorder::RecordMethodJitOrLoad(MethodDesc * pMethod, bool application)
 {
     STANDARD_VM_CONTRACT;
 
@@ -1103,7 +1103,7 @@ MulticoreJitCodeInfo MulticoreJitRecorder::RequestMethodCode(MethodDesc * pMetho
 
     if (!codeInfo.IsNull() && pManager->IsRecorderActive()) // recorder may be off when player is on (e.g. for Appx)
     {
-        RecordMethodJit(pMethod, false); // JITTed by background thread, returned to application
+        RecordMethodJitOrLoad(pMethod, false); // JITTed by background thread, returned to application
     }
 
     return codeInfo;
@@ -1426,7 +1426,7 @@ MulticoreJitCodeInfo MulticoreJitManager::RequestMethodCode(MethodDesc * pMethod
 // Call back from MethodDesc::MakeJitWorker for
 // Threading: protected by m_playerLock
 
-void MulticoreJitManager::RecordMethodJit(MethodDesc * pMethod)
+void MulticoreJitManager::RecordMethodJitOrLoad(MethodDesc * pMethod)
 {
     STANDARD_VM_CONTRACT;
 
@@ -1434,7 +1434,7 @@ void MulticoreJitManager::RecordMethodJit(MethodDesc * pMethod)
 
     if (m_pMulticoreJitRecorder != NULL)
     {
-        m_pMulticoreJitRecorder->RecordMethodJit(pMethod, true);
+        m_pMulticoreJitRecorder->RecordMethodJitOrLoad(pMethod, true);
 
         if (m_pMulticoreJitRecorder->IsAtFullCapacity())
         {
index 4e199a4..3b7bae0 100644 (file)
@@ -84,9 +84,9 @@ private:
     enum class TierInfo : TADDR
     {
         None = 0,
-        WasTier0Jit = 1 << 0,
+        WasTier0 = 1 << 0,
         JitSwitchedToOptimized = 1 << 1,
-        Mask = None | WasTier0Jit | JitSwitchedToOptimized
+        Mask = None | WasTier0 | JitSwitchedToOptimized
     };
 
     TADDR m_entryPointAndTierInfo;
@@ -118,12 +118,12 @@ public:
         return IsNull() ? NULL : PINSTRToPCODE(m_entryPointAndTierInfo & ~(TADDR)TierInfo::Mask);
     }
 
-    bool WasTier0Jit() const
+    bool WasTier0() const
     {
         WRAPPER_NO_CONTRACT;
         VerifyIsNotNull();
 
-        return (m_entryPointAndTierInfo & (TADDR)TierInfo::WasTier0Jit) != 0;
+        return (m_entryPointAndTierInfo & (TADDR)TierInfo::WasTier0) != 0;
     }
 
     bool JitSwitchedToOptimized() const
@@ -281,7 +281,7 @@ public:
 
     MulticoreJitCodeInfo RequestMethodCode(MethodDesc * pMethod);
 
-    void RecordMethodJit(MethodDesc * pMethod);
+    void RecordMethodJitOrLoad(MethodDesc * pMethod);
 
     MulticoreJitPlayerStat & GetStats()
     {
index cd00e6f..234493f 100644 (file)
@@ -705,7 +705,7 @@ public:
                (m_ModuleCount  >= MAX_MODULES);
     }
 
-    void RecordMethodJit(MethodDesc * pMethod, bool application);
+    void RecordMethodJitOrLoad(MethodDesc * pMethod, bool application);
 
     MulticoreJitCodeInfo RequestMethodCode(MethodDesc * pMethod, MulticoreJitManager * pManager);
 
index ab243cc..da75fe4 100644 (file)
@@ -87,7 +87,7 @@ void MulticoreJitCodeStorage::StoreMethodCode(MethodDesc * pMD, MulticoreJitCode
                 "%p %p %d %d StoredMethodCode",
                 pMD,
                 codeInfo.GetEntryPoint(),
-                (int)codeInfo.WasTier0Jit(),
+                (int)codeInfo.WasTier0(),
                 (int)codeInfo.JitSwitchedToOptimized()));
         }
 #endif
@@ -131,7 +131,7 @@ MulticoreJitCodeInfo MulticoreJitCodeStorage::QueryAndRemoveMethodCode(MethodDes
                     "%p %p %d %d QueryAndRemoveMethodCode",
                     pMethod,
                     codeInfo.GetEntryPoint(),
-                    (int)codeInfo.WasTier0Jit(),
+                    (int)codeInfo.WasTier0(),
                     (int)codeInfo.JitSwitchedToOptimized()));
             }
 #endif
@@ -521,7 +521,9 @@ HRESULT MulticoreJitProfilePlayer::HandleModuleRecord(const ModuleRecord * pMod)
 
 #ifndef DACCESS_COMPILE
 MulticoreJitPrepareCodeConfig::MulticoreJitPrepareCodeConfig(MethodDesc* pMethod) :
-    PrepareCodeConfig(NativeCodeVersion(pMethod), FALSE, FALSE), m_wasTier0Jit(false)
+    // Method code that was pregenerated and loaded is recorded in the multi-core JIT profile, so enable multi-core JIT to also
+    // look up pregenerated code to help parallelize the work
+    PrepareCodeConfig(NativeCodeVersion(pMethod), FALSE, TRUE), m_wasTier0(false)
 {
     WRAPPER_NO_CONTRACT;
 
@@ -548,9 +550,9 @@ MulticoreJitCodeInfo::MulticoreJitCodeInfo(PCODE entryPoint, const MulticoreJitP
     _ASSERTE((m_entryPointAndTierInfo & (TADDR)TierInfo::Mask) == 0);
 
 #ifdef FEATURE_TIERED_COMPILATION
-    if (pConfig->WasTier0Jit())
+    if (pConfig->WasTier0())
     {
-        m_entryPointAndTierInfo |= (TADDR)TierInfo::WasTier0Jit;
+        m_entryPointAndTierInfo |= (TADDR)TierInfo::WasTier0;
     }
 
     if (pConfig->JitSwitchedToOptimized())
@@ -890,7 +892,6 @@ HRESULT MulticoreJitProfilePlayer::HandleNonGenericMethodInfoRecord(unsigned mod
             // Similar to Module::FindMethod + Module::FindMethodThrowing,
             // except it calls GetMethodDescFromMemberDefOrRefOrSpec with strictMetadataChecks=FALSE to allow generic instantiation
             MethodDesc * pMethod = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(pModule, token, NULL, FALSE, FALSE);
-
             CompileMethodInfoRecord(pModule, pMethod, false);
         }
         else
@@ -1292,7 +1293,6 @@ HRESULT MulticoreJitProfilePlayer::PlayProfile()
                 unsigned curdata1 = * (const unsigned *) pCurBuf;
                 unsigned currcdTyp = curdata1 >> RECORD_TYPE_OFFSET;
                 unsigned curmoduleIndex = curdata1 & MODULE_MASK;
-                unsigned curflags = curdata1 & METHOD_FLAGS_MASK;
 
                 if (currcdTyp == MULTICOREJIT_METHOD_RECORD_ID)
                 {
index 01fa328..d4c347c 100644 (file)
@@ -470,18 +470,43 @@ PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig, bool shouldTier
         {
             LOG_USING_R2R_CODE(this);
 
+#ifdef FEATURE_TIERED_COMPILATION
+            // Finalize the optimization tier before SetNativeCode() is called
+            bool shouldCountCalls = shouldTier && pConfig->FinalizeOptimizationTierForTier0Load();
+#endif
+
             if (pConfig->SetNativeCode(pCode, &pCode))
             {
 #ifdef FEATURE_CODE_VERSIONING
                 pConfig->SetGeneratedOrLoadedNewCode();
 #endif
 #ifdef FEATURE_TIERED_COMPILATION
-                if (shouldTier)
+                if (shouldCountCalls)
                 {
                     _ASSERTE(pConfig->GetCodeVersion().GetOptimizationTier() == NativeCodeVersion::OptimizationTier0);
                     pConfig->SetShouldCountCalls();
                 }
 #endif
+
+#ifdef FEATURE_MULTICOREJIT
+                // Multi-core JIT is only applicable to the default code version. A method is recorded in the profile only when
+                // SetNativeCode() above succeeds to avoid recording duplicates in the multi-core JIT profile. Successful loads
+                // of R2R code are also recorded.
+                if (pConfig->NeedsMulticoreJitNotification())
+                {
+                    _ASSERTE(pConfig->GetCodeVersion().IsDefaultVersion());
+                    _ASSERTE(!pConfig->IsForMulticoreJit());
+
+                    MulticoreJitManager & mcJitManager = GetAppDomain()->GetMulticoreJitManager();
+                    if (mcJitManager.IsRecorderActive())
+                    {
+                        if (MulticoreJitManager::IsMethodSupported(this))
+                        {
+                            mcJitManager.RecordMethodJitOrLoad(this);
+                        }
+                    }
+                }
+#endif
             }
         }
     }
@@ -602,16 +627,17 @@ PCODE MethodDesc::GetPrecompiledR2RCode(PrepareCodeConfig* pConfig)
         }
     }
 #endif
+
     return pCode;
 }
 
-PCODE MethodDesc::GetMulticoreJitCode(PrepareCodeConfig* pConfig, bool* pWasTier0Jit)
+PCODE MethodDesc::GetMulticoreJitCode(PrepareCodeConfig* pConfig, bool* pWasTier0)
 {
     STANDARD_VM_CONTRACT;
     _ASSERTE(pConfig != NULL);
     _ASSERTE(pConfig->GetMethodDesc() == this);
-    _ASSERTE(pWasTier0Jit != NULL);
-    _ASSERTE(!*pWasTier0Jit);
+    _ASSERTE(pWasTier0 != NULL);
+    _ASSERTE(!*pWasTier0);
 
     MulticoreJitCodeInfo codeInfo;
 #ifdef FEATURE_MULTICOREJIT
@@ -625,9 +651,9 @@ PCODE MethodDesc::GetMulticoreJitCode(PrepareCodeConfig* pConfig, bool* pWasTier
         #ifdef FEATURE_TIERED_COMPILATION
             if (!codeInfo.IsNull())
             {
-                if (codeInfo.WasTier0Jit())
+                if (codeInfo.WasTier0())
                 {
-                    *pWasTier0Jit = true;
+                    *pWasTier0 = true;
                 }
                 if (codeInfo.JitSwitchedToOptimized())
                 {
@@ -803,13 +829,13 @@ PCODE MethodDesc::JitCompileCode(PrepareCodeConfig* pConfig)
             NativeCodeVersion codeVersion = pConfig->GetCodeVersion();
             if (codeVersion.IsDefaultVersion())
             {
-                bool wasTier0Jit = false;
-                pCode = GetMulticoreJitCode(pConfig, &wasTier0Jit);
+                bool wasTier0 = false;
+                pCode = GetMulticoreJitCode(pConfig, &wasTier0);
                 if (pCode != NULL)
                 {
                 #ifdef FEATURE_TIERED_COMPILATION
                     // Finalize the optimization tier before SetNativeCode() is called
-                    bool shouldCountCalls = wasTier0Jit && pConfig->FinalizeOptimizationTierForTier0Jit();
+                    bool shouldCountCalls = wasTier0 && pConfig->FinalizeOptimizationTierForTier0LoadOrJit();
                 #endif
 
                     if (pConfig->SetNativeCode(pCode, &pCode))
@@ -1079,7 +1105,7 @@ PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEn
 
 #ifdef FEATURE_TIERED_COMPILATION
     // Finalize the optimization tier before SetNativeCode() is called
-    bool shouldCountCalls = pFlags->IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0) && pConfig->FinalizeOptimizationTierForTier0Jit();
+    bool shouldCountCalls = pFlags->IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0) && pConfig->FinalizeOptimizationTierForTier0LoadOrJit();
 #endif
 
     // Aside from rejit, performing a SetNativeCodeInterlocked at this point
@@ -1126,7 +1152,7 @@ PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEn
         {
             if (MulticoreJitManager::IsMethodSupported(this))
             {
-                mcJitManager.RecordMethodJit(this); // Tell multi-core JIT manager to record method on successful JITting
+                mcJitManager.RecordMethodJitOrLoad(this); // Tell multi-core JIT manager to record method on successful JITting
             }
         }
     }
@@ -1325,10 +1351,27 @@ const char *PrepareCodeConfig::GetJitOptimizationTierStr(PrepareCodeConfig *conf
 }
 
 #ifdef FEATURE_TIERED_COMPILATION
+// This function should be called before SetNativeCode() for consistency with usage of FinalizeOptimizationTierForTier0Jit
+bool PrepareCodeConfig::FinalizeOptimizationTierForTier0Load()
+{
+    _ASSERTE(GetMethodDesc()->IsEligibleForTieredCompilation());
+    _ASSERTE(!JitSwitchedToOptimized());
+
+    if (!IsForMulticoreJit())
+    {
+        return true; // should count calls if SetNativeCode() succeeds
+    }
+
+    // When using multi-core JIT, the loaded code would not be used until the method is called. Record some information that may
+    // be used later when the method is called.
+    ((MulticoreJitPrepareCodeConfig *)this)->SetWasTier0();
+    return false; // don't count calls
+}
+
 // This function should be called before SetNativeCode() to update the optimization tier if necessary before SetNativeCode() is
 // called. As soon as SetNativeCode() is called, another thread may get the native code and the optimization tier for that code
 // version, and it should have already been finalized.
-bool PrepareCodeConfig::FinalizeOptimizationTierForTier0Jit()
+bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit()
 {
     _ASSERTE(GetMethodDesc()->IsEligibleForTieredCompilation());
 
@@ -1336,7 +1379,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0Jit()
     {
         // When using multi-core JIT, the jitted code would not be used until the method is called. Don't make changes to the
         // optimization tier yet, just record some information that may be used later when the method is called.
-        ((MulticoreJitPrepareCodeConfig *)this)->SetWasTier0Jit();
+        ((MulticoreJitPrepareCodeConfig *)this)->SetWasTier0();
         return false; // don't count calls
     }
 
@@ -2229,19 +2272,6 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo
         if (pCode == NULL)
         {
             pCode = GetStubForInteropMethod(this);
-#ifdef FEATURE_MULTICOREJIT
-            if (pCode)
-            {
-                MulticoreJitManager & mcJitManager = GetAppDomain()->GetMulticoreJitManager();
-                if (mcJitManager.IsRecorderActive())
-                {
-                    if (MulticoreJitManager::IsMethodSupported(this))
-                    {
-                        mcJitManager.RecordMethodJit(this); // Tell multi-core JIT manager to record method on successful JITting
-                    }
-                }
-            }
-#endif // FEATURE_MULTICOREJIT
         }
 
         GetOrCreatePrecode();