diff options
author | Bruce Forstall <brucefo@microsoft.com> | 2016-10-04 15:12:46 -0700 |
---|---|---|
committer | Bruce Forstall <brucefo@microsoft.com> | 2016-10-06 11:43:49 -0700 |
commit | c47362766e24ab4a7195fa1fb851d254c7b84373 (patch) | |
tree | ce2b2230f63b387856c81c026b4dbcef589020bb /src | |
parent | 920bf1330d0148fc5a277d7ea18f4b7c95c89258 (diff) | |
download | coreclr-c47362766e24ab4a7195fa1fb851d254c7b84373.tar.gz coreclr-c47362766e24ab4a7195fa1fb851d254c7b84373.tar.bz2 coreclr-c47362766e24ab4a7195fa1fb851d254c7b84373.zip |
Enable legacy JIT fallback for CoreCLR on Windows x86
This is to support the eventual case where RyuJIT/x86 becomes the default
x86 JIT, but we support fallback to the older, legacy JIT32/x86 (which
will be renamed compatjit.dll). This uses the same mechanism that was
built for .NET 4.6 when RyuJIT/x64 was shipped, and allowed fallback
to JIT64 as compatjit.dll.
However, we use a different configuration name to avoid conflicting
with the .NET 4.6 COMPlus_useLegacy name (and unintentionally causing
users to fall back to JIT64 with their .NET 4.6 apps).
The COMPlus variable is COMPlus_UseWindowsX86CoreLegacyJit=1. For the
dotnet.exe host, you can set `"System.JIT.UseWindowsX86CoreLegacyJit": true`
in the "configProperties" section of the app.runtimeconfig.json file.
There is a new COMPlus_RequireLegacyJit=1 option to aid testing JIT
fallback.
Diffstat (limited to 'src')
-rw-r--r-- | src/inc/clrconfigvalues.h | 6 | ||||
-rw-r--r-- | src/vm/codeman.cpp | 78 | ||||
-rw-r--r-- | src/vm/codeman.h | 8 | ||||
-rw-r--r-- | src/vm/jitinterface.cpp | 7 |
4 files changed, 82 insertions, 17 deletions
diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h index 1bd04197f5..3e166da9cb 100644 --- a/src/inc/clrconfigvalues.h +++ b/src/inc/clrconfigvalues.h @@ -477,6 +477,12 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseLegacyJit, W("useLegacyJit"), 0, "Set to 1 RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DisableNativeImageLoadList, W("DisableNativeImageLoadList"), "Refuse to load native images corresponding to one of the assemblies on this semicolon-delimited list of assembly names.", CLRConfig::REGUTIL_default) #endif +#if defined(FEATURE_CORECLR) && defined(_TARGET_X86_) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseWindowsX86CoreLegacyJit, W("UseWindowsX86CoreLegacyJit"), 0, "Set to 1 to do all JITing with compatjit.dll. Only applicable to Windows x86 .NET Core.") +#endif + +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_RequireLegacyJit, W("RequireLegacyJit"), 0, "Set to 1 to require the use of legacy JIT (via COMPlus_useLegacyJit=1 or COMPlus_UseWindowsX86CoreLegacyJit=1).") + CONFIG_STRING_INFO_EX(INTERNAL_JitValNumCSE, W("JitValNumCSE"), "Enables ValNum CSE for the specified methods", CLRConfig::REGUTIL_default) CONFIG_STRING_INFO_EX(INTERNAL_JitLexicalCSE, W("JitLexicalCSE"), "Enables Lexical CSE for the specified methods", CLRConfig::REGUTIL_default) CONFIG_DWORD_INFO_EX(INTERNAL_JitNoCSE, W("JitNoCSE"), 0, "", CLRConfig::REGUTIL_default) diff --git a/src/vm/codeman.cpp b/src/vm/codeman.cpp index 88bfd58e93..444dcf4fef 100644 --- a/src/vm/codeman.cpp +++ b/src/vm/codeman.cpp @@ -31,6 +31,10 @@ #include "debuginfostore.h" #include "strsafe.h" +#ifdef FEATURE_CORECLR +#include "configuration.h" +#endif + #ifdef _WIN64 #define CHECK_DUPLICATED_STRUCT_LAYOUTS #include "../debug/daccess/fntableaccess.h" @@ -1196,9 +1200,11 @@ EEJitManager::EEJitManager() #ifdef _TARGET_AMD64_ m_pEmergencyJumpStubReserveList = NULL; #endif -#ifdef _TARGET_AMD64_ +#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) m_JITCompilerOther = NULL; #endif + m_fLegacyJitUsed = FALSE; + #ifdef ALLOW_SXS_JIT m_alternateJit = NULL; m_AltJITCompiler = NULL; @@ -1356,7 +1362,7 @@ void EEJitManager::SetCpuInfo() enum JIT_LOAD_JIT_ID { JIT_LOAD_MAIN = 500, // The "main" JIT. Normally, this is named "clrjit.dll". Start at a number that is somewhat uncommon (i.e., not zero or 1) to help distinguish from garbage, in process dumps. - JIT_LOAD_LEGACY, // The "legacy" JIT. Normally, this is named "compatjit.dll" (aka, JIT64). This only applies to AMD64. + JIT_LOAD_LEGACY, // The "legacy" JIT. Normally, this is named "compatjit.dll". This applies to AMD64 on Windows desktop, or x86 on Windows .NET Core. JIT_LOAD_ALTJIT // An "altjit". By default, named "protojit.dll". Used both internally, as well as externally for JIT CTP builds. }; @@ -1432,33 +1438,43 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName, OUT HINSTANCE* phJit, OUT I #ifdef FEATURE_CORECLR PathString CoreClrFolderHolder; extern HINSTANCE g_hThisInst; + bool havePath = false; #if !defined(FEATURE_MERGE_JIT_AND_ENGINE) if (g_CLRJITPath != nullptr) { - // If we have been asked to load a specific JIT binary, load it. + // If we have been asked to load a specific JIT binary, load from that path. + // The main JIT load will use exactly that name because pwzJitName will have + // been computed as the last component of g_CLRJITPath by ExecutionManager::GetJitName(). + // Non-primary JIT names (such as compatjit or altjit) will be loaded from the + // same directory. + // (Ideally, g_CLRJITPath would just be the JIT path without the filename component, + // but that's not how the JIT_PATH variable was originally defined.) CoreClrFolderHolder.Set(g_CLRJITPath); + havePath = true; } else #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE) if (WszGetModuleFileName(g_hThisInst, CoreClrFolderHolder)) { // Load JIT from next to CoreCLR binary + havePath = true; + } + + if (havePath && !CoreClrFolderHolder.IsEmpty()) + { SString::Iterator iter = CoreClrFolderHolder.End(); BOOL findSep = CoreClrFolderHolder.FindBack(iter, DIRECTORY_SEPARATOR_CHAR_W); if (findSep) { SString sJitName(pwzJitName); CoreClrFolderHolder.Replace(iter + 1, CoreClrFolderHolder.End() - (iter + 1), sJitName); - } - } - if (!CoreClrFolderHolder.IsEmpty()) - { - *phJit = CLRLoadLibrary(CoreClrFolderHolder.GetUnicode()); - if (*phJit != NULL) - { - hr = S_OK; + *phJit = CLRLoadLibrary(CoreClrFolderHolder.GetUnicode()); + if (*phJit != NULL) + { + hr = S_OK; + } } } @@ -1614,7 +1630,7 @@ BOOL EEJitManager::LoadJIT() #else // !FEATURE_MERGE_JIT_AND_ENGINE m_JITCompiler = NULL; -#ifdef _TARGET_AMD64_ +#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) m_JITCompilerOther = NULL; #endif @@ -1623,8 +1639,8 @@ BOOL EEJitManager::LoadJIT() // Set as a courtesy to code:CorCompileGetRuntimeDll s_ngenCompilerDll = m_JITCompiler; - -#if defined(_TARGET_AMD64_) && !defined(CROSSGEN_COMPILE) && !defined(FEATURE_CORECLR) + +#if (defined(_TARGET_AMD64_) && !defined(CROSSGEN_COMPILE) && !defined(FEATURE_CORECLR)) || (defined(_TARGET_X86_) && defined(FEATURE_CORECLR)) // If COMPlus_UseLegacyJit=1, then we fall back to compatjit.dll. // // This fallback mechanism was introduced for Visual Studio "14" Preview, when JIT64 (the legacy JIT) was replaced with @@ -1645,8 +1661,16 @@ BOOL EEJitManager::LoadJIT() // is set, we also must use JIT64 for all NGEN compilations as well. // // See the document "RyuJIT Compatibility Fallback Specification.docx" for details. + // + // For .NET Core 1.2, RyuJIT for x86 is the primary jit (clrjit.dll) and JIT32 for x86 is the fallback, legacy JIT (compatjit.dll). + // Thus, the COMPlus_useLegacyJit=1 mechanism has been enabled for x86 CoreCLR. This scenario does not have the UseRyuJIT + // registry key, nor the AppX binder mode. +#if defined(FEATURE_CORECLR) + bool fUseRyuJit = true; +#else bool fUseRyuJit = UseRyuJit(); +#endif if ((!IsCompilationProcess() || !fUseRyuJit) && // Use RyuJIT for all NGEN, unless we're falling back to JIT64 for everything. (newJitCompiler != nullptr)) // the main JIT must successfully load before we try loading the fallback JIT @@ -1660,7 +1684,11 @@ BOOL EEJitManager::LoadJIT() if (!fUsingCompatJit) { +#if defined(FEATURE_CORECLR) + DWORD useLegacyJit = Configuration::GetKnobBooleanValue(W("System.JIT.UseWindowsX86CoreLegacyJit"), CLRConfig::EXTERNAL_UseWindowsX86CoreLegacyJit); +#else DWORD useLegacyJit = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_UseLegacyJit); // uncached access, since this code is run no more than one time +#endif if (useLegacyJit == 1) { fUsingCompatJit = TRUE; @@ -1702,10 +1730,13 @@ BOOL EEJitManager::LoadJIT() // Tell the main JIT to fall back to the "fallback" JIT compiler, in case some // obfuscator tries to directly call the main JIT's getJit() function. newJitCompiler->setRealJit(fallbackICorJitCompiler); + + // Now, the compat JIT will be used. + m_fLegacyJitUsed = TRUE; } } } -#endif // defined(_TARGET_AMD64_) && !defined(CROSSGEN_COMPILE) && !defined(FEATURE_CORECLR) +#endif // (defined(_TARGET_AMD64_) && !defined(CROSSGEN_COMPILE) && !defined(FEATURE_CORECLR)) || (defined(_TARGET_X86_) && defined(FEATURE_CORECLR)) #endif // !FEATURE_MERGE_JIT_AND_ENGINE @@ -4359,7 +4390,22 @@ LPCWSTR ExecutionManager::GetJitName() LPCWSTR pwzJitName = NULL; -#if !defined(FEATURE_CORECLR) +#if defined(FEATURE_CORECLR) +#if !defined(CROSSGEN_COMPILE) + if (g_CLRJITPath != nullptr) + { + const wchar_t* p = wcsrchr(g_CLRJITPath, DIRECTORY_SEPARATOR_CHAR_W); + if (p != nullptr) + { + pwzJitName = p + 1; // Return just the filename, not the directory name + } + else + { + pwzJitName = g_CLRJITPath; + } + } +#endif // !defined(CROSSGEN_COMPILE) +#else // !FEATURE_CORECLR // Try to obtain a name for the jit library from the env. variable IfFailThrow(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_JitName, const_cast<LPWSTR *>(&pwzJitName))); #endif // !FEATURE_CORECLR diff --git a/src/vm/codeman.h b/src/vm/codeman.h index aaf1be8bbf..f4e04b8c67 100644 --- a/src/vm/codeman.h +++ b/src/vm/codeman.h @@ -1193,10 +1193,16 @@ public: public: ICorJitCompiler * m_jit; HINSTANCE m_JITCompiler; -#ifdef _TARGET_AMD64_ +#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) HINSTANCE m_JITCompilerOther; // Stores the handle of the legacy JIT, if one is loaded. #endif + // TRUE if the legacy/compat JIT was loaded successfully and will be used. + // This is available in all builds so if COMPlus_RequireLegacyJit=1 is set in a test, + // the test will fail in any build where the legacy JIT is not loaded, even if legacy + // fallback is not available in that build. This prevents unexpected silent successes. + BOOL m_fLegacyJitUsed; + #ifdef ALLOW_SXS_JIT //put these at the end so that we don't mess up the offsets in the DAC. ICorJitCompiler * m_alternateJit; diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp index 2d28a1b37a..c39f9b31fd 100644 --- a/src/vm/jitinterface.cpp +++ b/src/vm/jitinterface.cpp @@ -12415,6 +12415,13 @@ PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* ILHeader, EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Failed to load JIT compiler")); #endif // ALLOW_SXS_JIT } + + // If no compatjit wasn't used, but the user (normally a test case) requires that one is used, then fail. + // This is analogous to ZapRequire. + if (!jitMgr->m_fLegacyJitUsed && (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_RequireLegacyJit) == 1)) + { + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Failed to use legacy JIT compiler with RequireLegacyJit set")); + } #endif // CROSSGEN_COMPILE #ifdef _DEBUG |