When QuickJit is enabled, disable it for methods that contain loops by default (dotne...
authorKoundinya Veluri <kouvel@users.noreply.github.com>
Thu, 2 May 2019 20:48:13 +0000 (13:48 -0700)
committerGitHub <noreply@github.com>
Thu, 2 May 2019 20:48:13 +0000 (13:48 -0700)
When QuickJit is enabled, disable it for methods that contain loops by default

Fixes https://github.com/dotnet/coreclr/issues/19751 by default when QuickJit is enabled
- Added config variable TC_QuickJitForLoops. When disabled (the default), the JIT identifies loops and explicit tail calls and switches to tier 1 JIT.
- This would prevent the possibility of spending too long in QuickJit code, but may decrease startup time a bit when QuickJit is enabled
- Removed TC_StartupTier_OptimizeCode, as now that there is TC_QuickJit, I didn't see a good use for it
- Removed references to "StartupTier" in config variables because we had previously decided not to call it that.
- When QuickJit is disabled, avoid creating native code slots for methods in non-R2R'ed modules, as tiering would be disabled for those anyway
- Marked TC_QuickJit config var as external

Commit migrated from https://github.com/dotnet/coreclr/commit/607c8db3931cc4f4afe086f2cd4dd957d35ef0ac

15 files changed:
docs/coreclr/project-docs/clr-configuration-knobs.md
src/coreclr/src/inc/clrconfigvalues.h
src/coreclr/src/inc/corinfo.h
src/coreclr/src/jit/compiler.cpp
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/flowgraph.cpp
src/coreclr/src/vm/callcounter.cpp
src/coreclr/src/vm/eeconfig.cpp
src/coreclr/src/vm/eeconfig.h
src/coreclr/src/vm/jitinterface.cpp
src/coreclr/src/vm/methodtablebuilder.cpp
src/coreclr/src/vm/prestub.cpp
src/coreclr/src/vm/tieredcompilation.cpp
src/coreclr/tests/src/JIT/Directed/IL/Tailcall/ExplicitTailCallNoSO.il [new file with mode: 0644]
src/coreclr/tests/src/JIT/Directed/IL/Tailcall/ExplicitTailCallNoSO.ilproj [new file with mode: 0644]

index 8eddfa7..61f4d00 100644 (file)
@@ -17,7 +17,7 @@ Name | Description | Type
 
 ## Environment/Registry Configuration Knobs
 
-This table was machine-generated using `clr-configuration-knobs.csx` script from repository commit [d064ffb](https://github.com/dotnet/coreclr/commit/d064ffb6b05c4f7fa44c7ee389e9694e64a76c08) on 15/04/2019. It might be out of date. To generate latest documentation run `{dotnet-script} clr-configuration-knobs.csx` from this file directory.
+This table was machine-generated using `clr-configuration-knobs.csx` script from repository commit [0ae3d02](https://github.com/dotnet/coreclr/commit/0ae3d020f82c3f8650b7e5eeaf9f1030f7e7e785) on 4/25/2019. It might be out of date. To generate latest documentation run `dotnet-script clr-configuration-knobs.csx` from this file directory.
 
 When using these configurations from environment variables, the variables need to have the `COMPlus_` prefix in their names. e.g. To set DumpJittedMethods to 1, add the environment variable `COMPlus_DumpJittedMethods=1`.
 
@@ -802,12 +802,12 @@ Name | Description | Type | Class | Default Value | Flags
 
 Name | Description | Type | Class | Default Value | Flags
 -----|-------------|------|-------|---------------|-------
-`TC_QuickJit` | For methods that would be jitted, enable using quick JIT when appropriate. | `DWORD` | `UNSUPPORTED` | `0` |
-`TC_StartupTier_CallCounting` | Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers | `DWORD` | `INTERNAL` | `1` |
-`TC_StartupTier_CallCountingDelayMs` | A perpetual delay in milliseconds that is applied call counting in the startup tier and jitting at higher tiers, while there is startup-like activity. | `DWORD` | `UNSUPPORTED` | `100` |
-`TC_StartupTier_CallCountThreshold` | Number of times a method must be called in the startup tier after which it is promoted to the next tier. | `DWORD` | `UNSUPPORTED` | `30` |
-`TC_StartupTier_DelaySingleProcMultiplier` | Multiplier for TC_StartupTier_CallCountingDelayMs that is applied on a single-processor machine or when the process is affinitized to a single processor. | `DWORD` | `UNSUPPORTED` | `10` |
-`TC_StartupTier_OptimizeCode` | Use optimized codegen (normally used by the optimized tier) in the startup tier | `DWORD` | `INTERNAL` | `0` |
+`TC_CallCounting` | Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers | `DWORD` | `INTERNAL` | `1` |
+`TC_CallCountingDelayMs` | A perpetual delay in milliseconds that is applied call counting in tier 0 and jitting at higher tiers, while there is startup-like activity. | `DWORD` | `INTERNAL` | `100` |
+`TC_CallCountThreshold` | Number of times a method must be called in tier 0 after which it is promoted to the next tier. | `DWORD` | `INTERNAL` | `30` |
+`TC_DelaySingleProcMultiplier` | Multiplier for TC_CallCountingDelayMs that is applied on a single-processor machine or when the process is affinitized to a single processor. | `DWORD` | `INTERNAL` | `10` |
+`TC_QuickJit` | For methods that would be jitted, enable using quick JIT when appropriate. | `DWORD` | `EXTERNAL` | `0` |
+`TC_QuickJitForLoops` | When quick JIT is enabled, quick JIT may also be used for methods that contain loops. | `DWORD` | `UNSUPPORTED` | `0` |
 `TieredCompilation` | Enables tiered compilation | `DWORD` | `EXTERNAL` | `1` |
 
 #### TypeLoader Configuration Knobs
index 3d8c269..ab4b8e4 100644 (file)
@@ -647,13 +647,12 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_HillClimbing_GainExponent,
 ///
 #ifdef FEATURE_TIERED_COMPILATION
 RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredCompilation, W("TieredCompilation"), 1, "Enables tiered compilation")
-RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_QuickJit, W("TC_QuickJit"), 0, "For methods that would be jitted, enable using quick JIT when appropriate.")
-RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_StartupTier_CallCountThreshold, W("TC_StartupTier_CallCountThreshold"), 30, "Number of times a method must be called in the startup tier after which it is promoted to the next tier.")
-RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_StartupTier_CallCountingDelayMs, W("TC_StartupTier_CallCountingDelayMs"), 100, "A perpetual delay in milliseconds that is applied call counting in the startup tier and jitting at higher tiers, while there is startup-like activity.")
-RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_StartupTier_DelaySingleProcMultiplier, W("TC_StartupTier_DelaySingleProcMultiplier"), 10, "Multiplier for TC_StartupTier_CallCountingDelayMs that is applied on a single-processor machine or when the process is affinitized to a single processor.")
-
-RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_StartupTier_CallCounting, W("TC_StartupTier_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers")
-RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_StartupTier_OptimizeCode, W("TC_StartupTier_OptimizeCode"), 0, "Use optimized codegen (normally used by the optimized tier) in the startup tier")
+RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_QuickJit, W("TC_QuickJit"), 0, "For methods that would be jitted, enable using quick JIT when appropriate.")
+RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_QuickJitForLoops, W("TC_QuickJitForLoops"), 0, "When quick JIT is enabled, quick JIT may also be used for methods that contain loops.")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountThreshold, W("TC_CallCountThreshold"), 30, "Number of times a method must be called in tier 0 after which it is promoted to the next tier.")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountingDelayMs, W("TC_CallCountingDelayMs"), 100, "A perpetual delay in milliseconds that is applied call counting in tier 0 and jitting at higher tiers, while there is startup-like activity.")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DelaySingleProcMultiplier, W("TC_DelaySingleProcMultiplier"), 10, "Multiplier for TC_CallCountingDelayMs that is applied on a single-processor machine or when the process is affinitized to a single processor.")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers")
 #endif
 
 ///
index df6adfe..1a223a1 100644 (file)
@@ -832,7 +832,7 @@ enum CorInfoFlag
     CORINFO_FLG_INTRINSIC             = 0x00400000, // This method MAY have an intrinsic ID
     CORINFO_FLG_CONSTRUCTOR           = 0x00800000, // This method is an instance or type initializer
     CORINFO_FLG_AGGRESSIVE_OPT        = 0x01000000, // The method may contain hot code and should be aggressively optimized if possible
-//  CORINFO_FLG_UNUSED                = 0x02000000,
+    CORINFO_FLG_DISABLE_TIER0_FOR_LOOPS = 0x02000000, // Indicates that tier 0 JIT should not be used for a method that contains a loop
     CORINFO_FLG_NOSECURITYWRAP        = 0x04000000, // The method requires no security checks
     CORINFO_FLG_DONT_INLINE           = 0x10000000, // The method should not be inlined
     CORINFO_FLG_DONT_INLINE_CALLER    = 0x20000000, // The method should not be inlined, nor should its callers. It cannot be tail called.
@@ -864,6 +864,7 @@ enum CorInfoMethodRuntimeFlags
     CORINFO_FLG_BAD_INLINEE         = 0x00000001, // The method is not suitable for inlining
     CORINFO_FLG_VERIFIABLE          = 0x00000002, // The method has verifiable code
     CORINFO_FLG_UNVERIFIABLE        = 0x00000004, // The method has unverifiable code
+    CORINFO_FLG_SWITCHED_TO_TIER1   = 0x00000008, // The JIT decided to switch to tier 1 for this method, when a different tier was requested
 };
 
 
index 378d83b..6b6a64c 100644 (file)
@@ -5814,6 +5814,8 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE            classPtr,
     info.compTotalHotCodeSize  = 0;
     info.compTotalColdCodeSize = 0;
 
+    fgHasBackwardJump = false;
+
 #ifdef DEBUG
     compCurBB = nullptr;
     lvaTable  = nullptr;
@@ -5946,6 +5948,12 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE            classPtr,
         goto _Next;
     }
 
+    if (fgHasBackwardJump && (info.compFlags & CORINFO_FLG_DISABLE_TIER0_FOR_LOOPS) != 0 && fgCanSwitchToTier1())
+    {
+        // Method likely has a loop, switch to the OptimizedTier to avoid spending too much time running slower code
+        fgSwitchToTier1();
+    }
+
     compSetOptimizationLevel();
 
 #if COUNT_BASIC_BLOCKS
index fed7003..d10f34d 100644 (file)
@@ -5296,6 +5296,13 @@ protected:
     void        fgInitBBLookup();
     BasicBlock* fgLookupBB(unsigned addr);
 
+    bool fgHasBackwardJump;
+
+    bool fgCanSwitchToTier1();
+    void fgSwitchToTier1();
+
+    bool fgMayExplicitTailCall();
+
     void fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, FixedBitVect* jumpTarget);
 
     void fgMarkBackwardJump(BasicBlock* startBlock, BasicBlock* endBlock);
index 236302c..c9348d8 100644 (file)
@@ -4253,6 +4253,94 @@ private:
 };
 
 //------------------------------------------------------------------------
+// fgCanSwitchToTier1: Determines if conditions are met to allow switching the opt level to tier 1
+//
+// Return Value:
+//    True if the opt level may be switched to tier 1, false otherwise
+//
+// Assumptions:
+//    - compInitOptions() has been called
+//    - compSetOptimizationLevel() has not been called
+//
+// Notes:
+//    This method is to be called at some point before compSetOptimizationLevel() to determine if the opt level may be
+//    changed based on information gathered in early phases.
+
+bool Compiler::fgCanSwitchToTier1()
+{
+    bool result = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_MIN_OPT) &&
+                  !opts.compDbgCode && !compIsForInlining();
+    if (result)
+    {
+        // Ensure that it would be safe to change the opt level
+        assert(opts.compFlags == CLFLG_MINOPT);
+        assert(!opts.IsMinOptsSet());
+    }
+
+    return result;
+}
+
+//------------------------------------------------------------------------
+// fgSwitchToTier1: Switch the opt level to tier 1
+//
+// Assumptions:
+//    - fgCanSwitchToTier1() is true
+//    - compSetOptimizationLevel() has not been called
+//
+// Notes:
+//    This method is to be called at some point before compSetOptimizationLevel() to switch the opt level to tier 1
+//    based on information gathered in early phases.
+
+void Compiler::fgSwitchToTier1()
+{
+    assert(fgCanSwitchToTier1());
+
+    // Switch to tier 1 and re-init options
+    assert(opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0));
+    opts.jitFlags->Clear(JitFlags::JIT_FLAG_TIER0);
+    opts.jitFlags->Set(JitFlags::JIT_FLAG_TIER1);
+    compInitOptions(opts.jitFlags);
+
+    // Notify the VM of the change
+    info.compCompHnd->setMethodAttribs(info.compMethodHnd, CORINFO_FLG_SWITCHED_TO_TIER1);
+}
+
+//------------------------------------------------------------------------
+// fgMayExplicitTailCall: Estimates conservatively for an explicit tail call, if the importer may actually use a tail
+// call.
+//
+// Return Value:
+//    - False if a tail call will not be generated
+//    - True if a tail call *may* be generated
+//
+// Assumptions:
+//    - compInitOptions() has been called
+//    - info.compIsVarArgs has been initialized
+//    - An explicit tail call has been seen
+//    - compSetOptimizationLevel() has not been called
+
+bool Compiler::fgMayExplicitTailCall()
+{
+    assert(!compIsForInlining());
+
+    if (info.compFlags & CORINFO_FLG_SYNCH)
+    {
+        // Caller is synchronized
+        return false;
+    }
+
+#if !FEATURE_FIXED_OUT_ARGS
+    if (info.compIsVarArgs)
+    {
+        // Caller is varargs
+        return false;
+    }
+#endif // FEATURE_FIXED_OUT_ARGS
+
+    return true;
+}
+
+//------------------------------------------------------------------------
 // fgFindJumpTargets: walk the IL stream, determining jump target offsets
 //
 // Arguments:
@@ -5137,6 +5225,7 @@ void Compiler::fgMarkBackwardJump(BasicBlock* startBlock, BasicBlock* endBlock)
         if ((block->bbFlags & BBF_BACKWARD_JUMP) == 0)
         {
             block->bbFlags |= BBF_BACKWARD_JUMP;
+            fgHasBackwardJump = true;
         }
     }
 }
@@ -5516,6 +5605,14 @@ unsigned Compiler::fgMakeBasicBlocks(const BYTE* codeAddr, IL_OFFSET codeSize, F
 #endif // !FEATURE_CORECLR && _TARGET_AMD64_
                     }
 
+                    if (fgCanSwitchToTier1() && fgMayExplicitTailCall())
+                    {
+                        // Method has an explicit tail call that may run like a loop or may not be generated as a tail
+                        // call in tier 0, switch to tier 1 to avoid spending too much time running slower code and to
+                        // avoid stack overflow from recursion
+                        fgSwitchToTier1();
+                    }
+
 #if !defined(FEATURE_CORECLR) && defined(_TARGET_AMD64_)
                     if (isCallPopAndRet)
                     {
index 4130345..d360f12 100644 (file)
@@ -39,7 +39,7 @@ bool CallCounter::IsEligibleForTier0CallCounting(MethodDesc* pMethodDesc)
     _ASSERTE(pMethodDesc != NULL);
     _ASSERTE(pMethodDesc->IsEligibleForTieredCompilation());
 
-    return g_pConfig->TieredCompilation_StartupTier_CallCounting() && !pMethodDesc->RequestedAggressiveOptimization();
+    return g_pConfig->TieredCompilation_CallCounting() && !pMethodDesc->RequestedAggressiveOptimization();
 }
 
 bool CallCounter::IsTier0CallCountingEnabled(MethodDesc* pMethodDesc)
@@ -131,7 +131,7 @@ void CallCounter::OnMethodCalled(
         if (pEntry == NULL)
         {
             isFirstTier0Call = true;
-            tier0CallCountLimit = (int)g_pConfig->TieredCompilation_StartupTier_CallCountThreshold() - 1;
+            tier0CallCountLimit = (int)g_pConfig->TieredCompilation_CallCountThreshold() - 1;
             _ASSERTE(tier0CallCountLimit >= 0);
             m_methodToCallCount.Add(CallCounterEntry(pMethodDesc, tier0CallCountLimit));
         }
index 0d4021f..a4b8e1d 100644 (file)
@@ -351,10 +351,10 @@ HRESULT EEConfig::Init()
 #if defined(FEATURE_TIERED_COMPILATION)
     fTieredCompilation = false;
     fTieredCompilation_QuickJit = false;
-    fTieredCompilation_StartupTier_CallCounting = false;
-    fTieredCompilation_StartupTier_OptimizeCode = false;
-    tieredCompilation_StartupTier_CallCountThreshold = 1;
-    tieredCompilation_StartupTier_CallCountingDelayMs = 0;
+    fTieredCompilation_QuickJitForLoops = false;
+    fTieredCompilation_CallCounting = false;
+    tieredCompilation_CallCountThreshold = 1;
+    tieredCompilation_CallCountingDelayMs = 0;
 #endif
 
 #ifndef CROSSGEN_COMPILE
@@ -1207,29 +1207,30 @@ HRESULT EEConfig::sync()
     dwSleepOnExit = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_SleepOnExit);
 
 #if defined(FEATURE_TIERED_COMPILATION)
-    fTieredCompilation = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredCompilation"), CLRConfig::EXTERNAL_TieredCompilation) != 0;
+    fTieredCompilation = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredCompilation"), CLRConfig::EXTERNAL_TieredCompilation);
 
     fTieredCompilation_QuickJit =
         Configuration::GetKnobBooleanValue(
             W("System.Runtime.TieredCompilation.QuickJit"),
-            CLRConfig::UNSUPPORTED_TC_QuickJit) != 0;
+            CLRConfig::EXTERNAL_TC_QuickJit);
+    fTieredCompilation_QuickJitForLoops =
+        Configuration::GetKnobBooleanValue(
+            W("System.Runtime.TieredCompilation.QuickJitForLoops"),
+            CLRConfig::UNSUPPORTED_TC_QuickJitForLoops);
 
-    fTieredCompilation_StartupTier_CallCounting = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TC_StartupTier_CallCounting) != 0;
-    fTieredCompilation_StartupTier_OptimizeCode = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TC_StartupTier_OptimizeCode) != 0;
+    fTieredCompilation_CallCounting = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TC_CallCounting) != 0;
 
-    tieredCompilation_StartupTier_CallCountThreshold =
-        CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_StartupTier_CallCountThreshold);
-    if (tieredCompilation_StartupTier_CallCountThreshold < 1)
+    tieredCompilation_CallCountThreshold = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TC_CallCountThreshold);
+    if (tieredCompilation_CallCountThreshold < 1)
     {
-        tieredCompilation_StartupTier_CallCountThreshold = 1;
+        tieredCompilation_CallCountThreshold = 1;
     }
-    else if (tieredCompilation_StartupTier_CallCountThreshold > INT_MAX) // CallCounter uses 'int'
+    else if (tieredCompilation_CallCountThreshold > INT_MAX) // CallCounter uses 'int'
     {
-        tieredCompilation_StartupTier_CallCountThreshold = INT_MAX;
+        tieredCompilation_CallCountThreshold = INT_MAX;
     }
 
-    tieredCompilation_StartupTier_CallCountingDelayMs =
-        CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_StartupTier_CallCountingDelayMs);
+    tieredCompilation_CallCountingDelayMs = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TC_CallCountingDelayMs);
 
 #ifndef FEATURE_PAL
     bool hadSingleProcessorAtStartup = CPUGroupInfo::HadSingleProcessorAtStartup();
@@ -1239,14 +1240,13 @@ HRESULT EEConfig::sync()
 
     if (hadSingleProcessorAtStartup)
     {
-        DWORD delayMultiplier =
-            CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_StartupTier_DelaySingleProcMultiplier);
+        DWORD delayMultiplier = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TC_DelaySingleProcMultiplier);
         if (delayMultiplier > 1)
         {
-            DWORD newDelay = tieredCompilation_StartupTier_CallCountingDelayMs * delayMultiplier;
-            if (newDelay / delayMultiplier == tieredCompilation_StartupTier_CallCountingDelayMs)
+            DWORD newDelay = tieredCompilation_CallCountingDelayMs * delayMultiplier;
+            if (newDelay / delayMultiplier == tieredCompilation_CallCountingDelayMs)
             {
-                tieredCompilation_StartupTier_CallCountingDelayMs = newDelay;
+                tieredCompilation_CallCountingDelayMs = newDelay;
             }
         }
     }
index aef34b3..f96a5a2 100644 (file)
@@ -285,10 +285,10 @@ public:
 #if defined(FEATURE_TIERED_COMPILATION)
     bool          TieredCompilation(void)           const { LIMITED_METHOD_CONTRACT;  return fTieredCompilation; }
     bool          TieredCompilation_QuickJit() const { LIMITED_METHOD_CONTRACT; return fTieredCompilation_QuickJit; }
-    bool          TieredCompilation_StartupTier_CallCounting()  const { LIMITED_METHOD_CONTRACT; return fTieredCompilation_StartupTier_CallCounting; }
-    bool          TieredCompilation_StartupTier_OptimizeCode() const { LIMITED_METHOD_CONTRACT; return fTieredCompilation_StartupTier_OptimizeCode; }
-    DWORD         TieredCompilation_StartupTier_CallCountThreshold() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_StartupTier_CallCountThreshold; }
-    DWORD         TieredCompilation_StartupTier_CallCountingDelayMs() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_StartupTier_CallCountingDelayMs; }
+    bool          TieredCompilation_QuickJitForLoops() const { LIMITED_METHOD_CONTRACT; return fTieredCompilation_QuickJitForLoops; }
+    bool          TieredCompilation_CallCounting()  const { LIMITED_METHOD_CONTRACT; return fTieredCompilation_CallCounting; }
+    DWORD         TieredCompilation_CallCountThreshold() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_CallCountThreshold; }
+    DWORD         TieredCompilation_CallCountingDelayMs() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_CallCountingDelayMs; }
 #endif
 
 #ifndef CROSSGEN_COMPILE
@@ -1018,10 +1018,10 @@ private: //----------------------------------------------------------------
 #if defined(FEATURE_TIERED_COMPILATION)
     bool fTieredCompilation;
     bool fTieredCompilation_QuickJit;
-    bool fTieredCompilation_StartupTier_CallCounting;
-    bool fTieredCompilation_StartupTier_OptimizeCode;
-    DWORD tieredCompilation_StartupTier_CallCountThreshold;
-    DWORD tieredCompilation_StartupTier_CallCountingDelayMs;
+    bool fTieredCompilation_QuickJitForLoops;
+    bool fTieredCompilation_CallCounting;
+    DWORD tieredCompilation_CallCountThreshold;
+    DWORD tieredCompilation_CallCountingDelayMs;
 #endif
 
 #ifndef CROSSGEN_COMPILE
index 11d5039..1b260b3 100644 (file)
@@ -6849,6 +6849,13 @@ DWORD CEEInfo::getMethodAttribsInternal (CORINFO_METHOD_HANDLE ftn)
         result |= CORINFO_FLG_DELEGATE_INVOKE;
     }
 
+#ifndef CROSSGEN_COMPILE
+    if (!g_pConfig->TieredCompilation_QuickJitForLoops())
+    {
+        result |= CORINFO_FLG_DISABLE_TIER0_FOR_LOOPS;
+    }
+#endif
+
     return result;
 }
 
@@ -6901,6 +6908,14 @@ void CEEInfo::setMethodAttribs (
         }
     }
 
+#ifdef FEATURE_TIERED_COMPILATION
+    if (attribs & CORINFO_FLG_SWITCHED_TO_TIER1)
+    {
+        _ASSERTE(ftn->IsEligibleForTieredCompilation());
+        ftn->GetCallCounter()->DisableTier0CallCounting(ftn);
+    }
+#endif
+
     EE_TO_JIT_TRANSITION();
 }
 
index 5b094f6..b2e7a07 100644 (file)
@@ -6959,6 +6959,11 @@ MethodTableBuilder::NeedsNativeCodeSlot(bmtMDMethod * pMDMethod)
 #ifdef FEATURE_TIERED_COMPILATION
     // Keep in-sync with MethodDesc::DetermineAndSetIsEligibleForTieredCompilation()
     if (g_pConfig->TieredCompilation() &&
+
+        // Policy - If QuickJit is disabled and the module is not ReadyToRun, the method would be ineligible for tiering
+        // currently to avoid some unnecessary overhead
+        (g_pConfig->TieredCompilation_QuickJit() || GetModule()->IsReadyToRun()) &&
+
         (pMDMethod->GetMethodType() == METHOD_TYPE_NORMAL || pMDMethod->GetMethodType() == METHOD_TYPE_INSTANTIATED))
     {
         return TRUE;
index 4adf32e..17174ad 100644 (file)
@@ -998,6 +998,21 @@ PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEn
         return pOtherCode;
     }
 
+#ifdef FEATURE_TIERED_COMPILATION
+    if (pFlags->IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0))
+    {
+        MethodDesc *methodDesc = pConfig->GetMethodDesc();
+        _ASSERTE(methodDesc->IsEligibleForTieredCompilation());
+
+        // Update the tier in the code version. The JIT may have decided to switch from tier 0 to tier 1, in which case call
+        // counting would have been disabled for the method.
+        if (!methodDesc->GetCallCounter()->IsTier0CallCountingEnabled(methodDesc))
+        {
+            pConfig->GetCodeVersion().SetOptimizationTier(NativeCodeVersion::OptimizationTier1);
+        }
+    }
+#endif
+
 #if defined(FEATURE_JIT_PITCHING)
     SavePitchingCandidate(this, *pSizeOfCode);
 #endif
index 467125f..91cfff4 100644 (file)
@@ -84,8 +84,6 @@ void TieredCompilationManager::Init()
         MODE_PREEMPTIVE;
     }
     CONTRACTL_END;
-
-    CrstHolder holder(&m_lock);
 }
 
 #endif // FEATURE_TIERED_COMPILATION && !DACCESS_COMPILE
@@ -109,7 +107,7 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza
         return NativeCodeVersion::OptimizationTier1;
     }
 
-    if (!g_pConfig->TieredCompilation_StartupTier_CallCounting())
+    if (!g_pConfig->TieredCompilation_CallCounting())
     {
         // Call counting is disabled altogether through config, the intention is to remain at the initial tier
         return NativeCodeVersion::OptimizationTier0;
@@ -150,7 +148,7 @@ void TieredCompilationManager::OnTier0MethodCalled(
         // Stop call counting when the delay is in effect
         IsTieringDelayActive() ||
         // Initiate the delay on tier 0 activity (when a new eligible method is called the first time)
-        (isFirstCall && g_pConfig->TieredCompilation_StartupTier_CallCountingDelayMs() != 0) ||
+        (isFirstCall && g_pConfig->TieredCompilation_CallCountingDelayMs() != 0) ||
         // Stop call counting when ready for tier 1 promotion
         currentCallCountLimit <= 0;
 
@@ -168,7 +166,7 @@ void TieredCompilationManager::OnMethodCallCountingStoppedWithoutTierPromotion(M
     _ASSERTE(pMethodDesc != nullptr);
     _ASSERTE(pMethodDesc->IsEligibleForTieredCompilation());
 
-    if (g_pConfig->TieredCompilation_StartupTier_CallCountingDelayMs() == 0 ||
+    if (g_pConfig->TieredCompilation_CallCountingDelayMs() == 0 ||
         !pMethodDesc->GetCallCounter()->IsCallCountingEnabled(pMethodDesc))
     {
         return;
@@ -314,7 +312,7 @@ bool TieredCompilationManager::TryInitiateTieringDelay()
 {
     WRAPPER_NO_CONTRACT;
     _ASSERTE(g_pConfig->TieredCompilation());
-    _ASSERTE(g_pConfig->TieredCompilation_StartupTier_CallCountingDelayMs() != 0);
+    _ASSERTE(g_pConfig->TieredCompilation_CallCountingDelayMs() != 0);
 
     NewHolder<SArray<MethodDesc*>> methodsPendingCountingHolder = new(nothrow) SArray<MethodDesc*>();
     if (methodsPendingCountingHolder == nullptr)
@@ -364,7 +362,7 @@ bool TieredCompilationManager::TryInitiateTieringDelay()
                     &m_tieringDelayTimerHandle,
                     TieringDelayTimerCallback,
                     timerContextHolder,
-                    g_pConfig->TieredCompilation_StartupTier_CallCountingDelayMs(),
+                    g_pConfig->TieredCompilation_CallCountingDelayMs(),
                     (DWORD)-1 /* Period, non-repeating */,
                     0 /* flags */))
             {
@@ -448,7 +446,7 @@ void TieredCompilationManager::TieringDelayTimerCallbackWorker()
         {
             if (ThreadpoolMgr::ChangeTimerQueueTimer(
                     tieringDelayTimerHandle,
-                    g_pConfig->TieredCompilation_StartupTier_CallCountingDelayMs(),
+                    g_pConfig->TieredCompilation_CallCountingDelayMs(),
                     (DWORD)-1 /* Period, non-repeating */))
             {
                 success = true;
@@ -818,8 +816,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(NativeCodeVersion nativeCodeV
         return flags;
     }
     
-    if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 &&
-        !g_pConfig->TieredCompilation_StartupTier_OptimizeCode())
+    if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0)
     {
         flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0);
     }
diff --git a/src/coreclr/tests/src/JIT/Directed/IL/Tailcall/ExplicitTailCallNoSO.il b/src/coreclr/tests/src/JIT/Directed/IL/Tailcall/ExplicitTailCallNoSO.il
new file mode 100644 (file)
index 0000000..562bddd
--- /dev/null
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern System.Runtime
+{
+  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
+  .ver 4:2:1:0
+}
+.assembly ExplicitTailCallNoSO
+{
+}
+.class private abstract auto ansi sealed beforefieldinit ExplicitTailCallNoSO
+       extends [System.Runtime]System.Object
+{
+  .method private hidebysig static int32 
+          Main() cil managed
+  {
+    .entrypoint
+    // Code size       44 (0x2c)
+    .maxstack  2
+    .locals init (int32 V_0,
+             int32 V_1,
+             int32 V_2)
+    IL_0000:  ldc.i4     0xf4240 // 1_000_000
+    IL_0005:  ldc.i4.0
+    IL_0006:  call       int32 ExplicitTailCallNoSO::CalculateSumWithTailCall(int32,
+                                                                              int32)
+    IL_000b:  stloc.0
+    IL_000c:  ldc.i4.0
+    IL_000d:  stloc.1
+    IL_000e:  ldc.i4.1
+    IL_000f:  stloc.2
+    IL_0010:  br.s       IL_001a
+
+    IL_0012:  ldloc.1
+    IL_0013:  ldloc.2
+    IL_0014:  add
+    IL_0015:  stloc.1
+    IL_0016:  ldloc.2
+    IL_0017:  ldc.i4.1
+    IL_0018:  add
+    IL_0019:  stloc.2
+    IL_001a:  ldloc.2
+    IL_001b:  ldc.i4     0xf4240 // 1_000_000
+    IL_0020:  ble.s      IL_0012
+
+    IL_0022:  ldloc.0
+    IL_0023:  ldloc.1
+    IL_0024:  beq.s      IL_0029
+
+    IL_0026:  ldc.i4.s   101
+    IL_0028:  ret
+
+    IL_0029:  ldc.i4.s   100
+    IL_002b:  ret
+  } // end of method ExplicitTailCallNoSO::Main
+
+  .method private hidebysig static int32 
+          CalculateSumWithTailCall(int32 n,
+                                   [opt] int32 sum) cil managed
+  {
+    .param [2] = int32(0x00000000)
+    // Code size       27 (0x1b)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldc.i4.0
+    IL_0002:  bgt.s      IL_0006
+
+    IL_0004:  ldc.i4.0
+    IL_0005:  ret
+
+    IL_0006:  ldarg.1
+    IL_0007:  ldarg.0
+    IL_0008:  add
+    IL_0009:  starg.s    sum
+    IL_000b:  ldarg.0
+    IL_000c:  ldc.i4.1
+    IL_000d:  bne.un.s   IL_0011
+
+    IL_000f:  ldarg.1
+    IL_0010:  ret
+
+    IL_0011:  ldarg.0
+    IL_0012:  ldc.i4.1
+    IL_0013:  sub
+    IL_0014:  ldarg.1
+    IL_0015:  tail. call int32 ExplicitTailCallNoSO::CalculateSumWithTailCall(int32,
+                                                                              int32)
+    IL_001a:  ret
+  } // end of method ExplicitTailCallNoSO::CalculateSumWithTailCall
+
+} // end of class ExplicitTailCallNoSO
diff --git a/src/coreclr/tests/src/JIT/Directed/IL/Tailcall/ExplicitTailCallNoSO.ilproj b/src/coreclr/tests/src/JIT/Directed/IL/Tailcall/ExplicitTailCallNoSO.ilproj
new file mode 100644 (file)
index 0000000..8e83faf
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{7DFE5715-DC0F-46D5-9D72-6176FE22BB6C}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+  <PropertyGroup>
+    <DebugType>PdbOnly</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ExplicitTailCallNoSO.il" />
+  </ItemGroup>
+  <PropertyGroup>
+    <CLRTestBatchPreCommands><![CDATA[
+$(CLRTestBatchPreCommands)
+set COMPlus_TC_QuickJit=1
+set COMPlus_TC_QuickJitForLoops=1
+]]></CLRTestBatchPreCommands>
+    <BashCLRTestPreCommands><![CDATA[
+$(BashCLRTestPreCommands)
+export COMPlus_TC_QuickJit=1
+export COMPlus_TC_QuickJitForLoops=1
+]]></BashCLRTestPreCommands>
+  </PropertyGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
\ No newline at end of file