#include "excep.h"
#include "log.h"
#include "win32threadpool.h"
+#include "threadsuspend.h"
#include "tieredcompilation.h"
// TieredCompilationManager determines which methods should be recompiled and
{
return TRUE; // stop notifications for this method
}
+ AsyncPromoteMethodToTier1(pMethodDesc);
+ return TRUE;
+}
+
+void TieredCompilationManager::AsyncPromoteMethodToTier1(MethodDesc* pMethodDesc)
+{
+ STANDARD_VM_CONTRACT;
+
+ NativeCodeVersion t1NativeCodeVersion;
+
+ // Add an inactive native code entry in the versioning table to track the tier1
+ // compilation we are going to create. This entry binds the compilation to a
+ // particular version of the IL code regardless of any changes that may
+ // occur between now and when jitting completes. If the IL does change in that
+ // interval the new code entry won't be activated.
+ {
+ CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(pMethodDesc);
+ NativeCodeVersionCollection nativeVersions = ilVersion.GetNativeCodeVersions(pMethodDesc);
+ for (NativeCodeVersionIterator cur = nativeVersions.Begin(), end = nativeVersions.End(); cur != end; cur++)
+ {
+ if (cur->GetOptimizationTier() == NativeCodeVersion::OptimizationTier1)
+ {
+ // we've already promoted
+ return;
+ }
+ }
+
+ if (FAILED(ilVersion.AddNativeCodeVersion(pMethodDesc, &t1NativeCodeVersion)))
+ {
+ // optimization didn't work for some reason (presumably OOM)
+ // just give up and continue on
+ return;
+ }
+ t1NativeCodeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTier1);
+ }
// Insert the method into the optimization queue and trigger a thread to service
// the queue if needed.
// unserviced. Synchronous retries appear unlikely to offer any material improvement
// and complicating the code to narrow an already rare error case isn't desirable.
{
- SListElem<MethodDesc*>* pMethodListItem = new (nothrow) SListElem<MethodDesc*>(pMethodDesc);
+ SListElem<NativeCodeVersion>* pMethodListItem = new (nothrow) SListElem<NativeCodeVersion>(t1NativeCodeVersion);
SpinLockHolder holder(&m_lock);
if (pMethodListItem != NULL)
{
}
else
{
- return TRUE; // stop notifications for this method
+ return;
}
}
}
EX_END_CATCH(RethrowTerminalExceptions);
- return TRUE; // stop notifications for this method
+ return;
}
void TieredCompilationManager::OnAppDomainShutdown()
{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END
+
SpinLockHolder holder(&m_lock);
m_isAppDomainShuttingDown = TRUE;
}
}
ULONGLONG startTickCount = CLRGetTickCount64();
- MethodDesc* pMethod = NULL;
+ NativeCodeVersion nativeCodeVersion;
EX_TRY
{
+ GCX_COOP();
ENTER_DOMAIN_ID(m_domainId);
{
+ GCX_PREEMP();
while (true)
{
{
SpinLockHolder holder(&m_lock);
- pMethod = GetNextMethodToOptimize();
- if (pMethod == NULL ||
+ nativeCodeVersion = GetNextMethodToOptimize();
+ if (nativeCodeVersion.IsNull() ||
m_isAppDomainShuttingDown)
{
m_countOptimizationThreadsRunning--;
}
}
- OptimizeMethod(pMethod);
+ OptimizeMethod(nativeCodeVersion);
// If we have been running for too long return the thread to the threadpool and queue another event
// This gives the threadpool a chance to service other requests on this thread before returning to
{
STRESS_LOG2(LF_TIEREDCOMPILATION, LL_ERROR, "TieredCompilationManager::OptimizeMethodsCallback: "
"Unhandled exception during method optimization, hr=0x%x, last method=%pM\n",
- GET_EXCEPTION()->GetHR(), pMethod);
+ GET_EXCEPTION()->GetHR(), nativeCodeVersion.GetMethodDesc());
}
EX_END_CATCH(RethrowTerminalExceptions);
}
// Jit compiles and installs new optimized code for a method.
// Called on a background thread.
-void TieredCompilationManager::OptimizeMethod(MethodDesc* pMethod)
+void TieredCompilationManager::OptimizeMethod(NativeCodeVersion nativeCodeVersion)
{
STANDARD_VM_CONTRACT;
- _ASSERTE(pMethod->IsEligibleForTieredCompilation());
- PCODE pJittedCode = CompileMethod(pMethod);
- if (pJittedCode != NULL)
+ _ASSERTE(nativeCodeVersion.GetMethodDesc()->IsEligibleForTieredCompilation());
+ if (CompileCodeVersion(nativeCodeVersion))
{
- InstallMethodCode(pMethod, pJittedCode);
+ ActivateCodeVersion(nativeCodeVersion);
}
}
// Compiles new optimized code for a method.
// Called on a background thread.
-PCODE TieredCompilationManager::CompileMethod(MethodDesc* pMethod)
+BOOL TieredCompilationManager::CompileCodeVersion(NativeCodeVersion nativeCodeVersion)
{
STANDARD_VM_CONTRACT;
PCODE pCode = NULL;
- ULONG sizeOfCode = 0;
+ MethodDesc* pMethod = nativeCodeVersion.GetMethodDesc();
EX_TRY
{
- CORJIT_FLAGS flags = CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_MCJIT_BACKGROUND);
- flags.Add(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_SPEED_OPT));
-
- if (pMethod->IsDynamicMethod())
- {
- ILStubResolver* pResolver = pMethod->AsDynamicMethodDesc()->GetILStubResolver();
- flags.Add(pResolver->GetJitFlags());
- COR_ILMETHOD_DECODER* pILheader = pResolver->GetILHeader();
- pCode = UnsafeJitFunction(pMethod, pILheader, flags, &sizeOfCode);
- }
- else
- {
- COR_ILMETHOD_DECODER::DecoderStatus status;
- COR_ILMETHOD_DECODER header(pMethod->GetILHeader(), pMethod->GetModule()->GetMDImport(), &status);
- pCode = UnsafeJitFunction(pMethod, &header, flags, &sizeOfCode);
- }
+ pCode = pMethod->PrepareCode(nativeCodeVersion);
}
EX_CATCH
{
}
EX_END_CATCH(RethrowTerminalExceptions)
- return pCode;
+ return pCode != NULL;
}
// Updates the MethodDesc and precode so that future invocations of a method will
// execute the native code pointed to by pCode.
// Called on a background thread.
-void TieredCompilationManager::InstallMethodCode(MethodDesc* pMethod, PCODE pCode)
+void TieredCompilationManager::ActivateCodeVersion(NativeCodeVersion nativeCodeVersion)
{
STANDARD_VM_CONTRACT;
- _ASSERTE(!pMethod->IsNativeCodeStableAfterInit());
+ MethodDesc* pMethod = nativeCodeVersion.GetMethodDesc();
+ CodeVersionManager* pCodeVersionManager = pMethod->GetCodeVersionManager();
- PCODE pExistingCode = pMethod->GetNativeCode();
- if (!pMethod->SetNativeCodeInterlocked(pCode, pExistingCode))
+ // If the ilParent version is active this will activate the native code version now.
+ // Otherwise if the ilParent version becomes active again in the future the native
+ // code version will activate then.
+ ILCodeVersion ilParent;
+ HRESULT hr = S_OK;
{
- //We aren't there yet, but when the feature is finished we shouldn't be racing against any other code mutator and there would be no
- //reason for this to fail
- STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "TieredCompilationManager::InstallMethodCode: Method %pM failed to update native code slot. Code=%pK\n",
- pMethod, pCode);
+ // As long as we are exclusively using precode publishing for tiered compilation
+ // methods this first attempt should succeed
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ ilParent = nativeCodeVersion.GetILCodeVersion();
+ hr = ilParent.SetActiveNativeCodeVersion(nativeCodeVersion, FALSE);
}
- else
+ if (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED)
{
- Precode* pPrecode = pMethod->GetPrecode();
- if (!pPrecode->SetTargetInterlocked(pCode, FALSE))
+ // if we start using jump-stamp publishing for tiered compilation, the first attempt
+ // without the runtime suspended will fail and then this second attempt will
+ // succeed.
+ // Even though this works performance is likely to be quite bad. Realistically
+ // we are going to need batched updates to makes tiered-compilation + jump-stamp
+ // viable. This fallback path is just here as a proof-of-concept.
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
{
- //We aren't there yet, but when the feature is finished we shouldn't be racing against any other code mutator and there would be no
- //reason for this to fail
- STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "TieredCompilationManager::InstallMethodCode: Method %pM failed to update precode. Code=%pK\n",
- pMethod, pCode);
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ hr = ilParent.SetActiveNativeCodeVersion(nativeCodeVersion, TRUE);
}
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ }
+ if (FAILED(hr))
+ {
+ STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "TieredCompilationManager::ActivateCodeVersion: Method %pM failed to publish native code for native code version %d\n",
+ pMethod, nativeCodeVersion.GetVersionId());
}
}
// Dequeues the next method in the optmization queue.
// This should be called with m_lock already held and runs
// on the background thread.
-MethodDesc* TieredCompilationManager::GetNextMethodToOptimize()
+NativeCodeVersion TieredCompilationManager::GetNextMethodToOptimize()
{
STANDARD_VM_CONTRACT;
- SListElem<MethodDesc*>* pElem = m_methodsToOptimize.RemoveHead();
+ SListElem<NativeCodeVersion>* pElem = m_methodsToOptimize.RemoveHead();
if (pElem != NULL)
{
- MethodDesc* pMD = pElem->GetValue();
+ NativeCodeVersion nativeCodeVersion = pElem->GetValue();
delete pElem;
- return pMD;
+ return nativeCodeVersion;
+ }
+ return NativeCodeVersion();
+}
+
+//static
+CORJIT_FLAGS TieredCompilationManager::GetJitFlags(NativeCodeVersion nativeCodeVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ CORJIT_FLAGS flags;
+ if (!nativeCodeVersion.GetMethodDesc()->IsEligibleForTieredCompilation())
+ {
+#ifdef FEATURE_INTERPRETER
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE);
+#endif
+ return flags;
+ }
+
+ if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0)
+ {
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0);
+ }
+ else
+ {
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1);
+#ifdef FEATURE_INTERPRETER
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE);
+#endif
}
- return NULL;
+ return flags;
}
#endif // FEATURE_TIERED_COMPILATION